diff options
Diffstat (limited to 'indra/newview/llviewerwindow.cpp')
-rw-r--r-- | indra/newview/llviewerwindow.cpp | 4816 |
1 files changed, 4816 insertions, 0 deletions
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp new file mode 100644 index 0000000000..3836b2d1d7 --- /dev/null +++ b/indra/newview/llviewerwindow.cpp @@ -0,0 +1,4816 @@ +/** + * @file llviewerwindow.cpp + * @brief Implementation of the LLViewerWindow class. + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +// system library includes +#include <stdio.h> +#include <iostream> +#include <fstream> + +#include "llviewerwindow.h" +#include "llviewquery.h" +#include "llxmltree.h" +#include "llviewercamera.h" +//#include "imdebug.h" + +#ifdef SABINRIG +#include "cbw.h" +#endif //SABINRIG + +// +// TODO: Many of these includes are unnecessary. Remove them. +// + +// linden library includes +#include "audioengine.h" // mute on minimize +#include "indra_constants.h" +#include "linked_lists.h" +#include "llassetstorage.h" +#include "llfontgl.h" +#include "llmediaengine.h" // mute on minimize +#include "llrect.h" +#include "llsky.h" +#include "llstring.h" +#include "llui.h" +#include "lluuid.h" +#include "llview.h" +#include "llxfermanager.h" +#include "message.h" +#include "object_flags.h" +#include "text_out.h" +#include "lltimer.h" +#include "timing.h" +#include "llviewermenu.h" + +// newview includes +#include "llagent.h" +#include "llalertdialog.h" +#include "llbox.h" +#include "llcameraview.h" +#include "llchatbar.h" +#include "llconsole.h" +#include "llviewercontrol.h" +#include "llcylinder.h" +#include "lldebugview.h" +#include "lldir.h" +#include "lldrawable.h" +#include "lldrawpoolalpha.h" +#include "lldrawpoolbump.h" +#include "lldrawpoolwater.h" +#include "llmaniptranslate.h" +#include "llface.h" +#include "llfeaturemanager.h" +#include "llfilepicker.h" +#include "llfloater.h" +#include "llfloaterbuildoptions.h" +#include "llfloaterchat.h" +#include "llfloatercustomize.h" +#include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor +#include "llfloaterland.h" +#include "llfloatermap.h" +#include "llfloatermute.h" +#include "llfloaternamedesc.h" +#include "llfloatersnapshot.h" +#include "llfloatertools.h" +#include "llfloaterworldmap.h" +#include "llfocusmgr.h" +#include "llframestatview.h" +#include "llgesturemgr.h" +#include "llglheaders.h" +#include "llhippo.h" +#include "llhoverview.h" +#include "llhudmanager.h" +#include "llhudview.h" +#include "llimagebmp.h" +#include "llimagej2c.h" +#include "llinventoryview.h" +#include "llkeyboard.h" +#include "lllineeditor.h" +#include "llmenugl.h" +#include "llmodaldialog.h" +#include "llmorphview.h" +#include "llmoveview.h" +#include "llnotify.h" +#include "lloverlaybar.h" +#include "llpreviewtexture.h" +#include "llprogressview.h" +#include "llresmgr.h" +#include "llrootview.h" +#include "llselectmgr.h" +#include "llsphere.h" +#include "llstartup.h" +#include "llstatusbar.h" +#include "llstatview.h" +#include "llsurface.h" +#include "llsurfacepatch.h" +#include "llimview.h" +#include "lltexlayer.h" +#include "lltextbox.h" +#include "lltextureview.h" +#include "lltool.h" +#include "lltoolbar.h" +#include "lltoolcomp.h" +#include "lltooldraganddrop.h" +#include "lltoolface.h" +#include "lltoolfocus.h" +#include "lltoolgrab.h" +#include "lltoolmgr.h" +#include "lltoolmorph.h" +#include "lltoolpie.h" +#include "lltoolplacer.h" +#include "lltoolselect.h" +#include "lltoolselectland.h" +#include "lltoolview.h" +#include "llvieweruictrlfactory.h" +#include "lluploaddialog.h" +#include "llviewercamera.h" +#include "llviewergesture.h" +#include "llviewerimagelist.h" +#include "llviewerinventory.h" +#include "llviewerkeyboard.h" +#include "llviewermenu.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" +#include "llviewerstats.h" +#include "llvoavatar.h" +#include "llvovolume.h" +#include "llworld.h" +#include "llworldmapview.h" +#include "moviemaker.h" +#include "pipeline.h" +#include "viewer.h" + +#if LL_WINDOWS +#include "llwindebug.h" +#include <tchar.h> // For Unicode conversion methods +#endif + +// +// Globals +// + +LLBottomPanel* gBottomPanel = NULL; + +extern BOOL gDebugClicks; +extern BOOL gDisplaySwapBuffers; +extern S32 gJamesInt; + +LLViewerWindow *gViewerWindow = NULL; +LLVelocityBar *gVelocityBar = NULL; + +LLVector3d gLastHitPosGlobal; +LLVector3d gLastHitObjectOffset; +LLUUID gLastHitObjectID; +S32 gLastHitObjectFace = -1; +BOOL gLastHitLand = FALSE; +F32 gLastHitUCoord; +F32 gLastHitVCoord; + + +LLVector3d gLastHitNonFloraPosGlobal; +LLVector3d gLastHitNonFloraObjectOffset; +LLUUID gLastHitNonFloraObjectID; +S32 gLastHitNonFloraObjectFace = -1; +BOOL gLastHitParcelWall = FALSE; + +S32 gLastHitUIElement = 0; +LLHUDIcon* gLastHitHUDIcon = NULL; + +BOOL gDebugSelect = FALSE; +U8 gLastPickAlpha = 255; +BOOL gUseGLPick = FALSE; + +// On the next pick pass (whenever that happens) +// should we try to pick individual faces? +// Cleared to FALSE every time a pick happens. +BOOL gPickFaces = FALSE; + +LLFrameTimer gMouseIdleTimer; +LLFrameTimer gAwayTimer; +LLFrameTimer gAwayTriggerTimer; +LLFrameTimer gAlphaFadeTimer; + +BOOL gShowOverlayTitle = FALSE; +BOOL gPickTransparent = TRUE; + +BOOL gDebugFastUIRender = FALSE; + +BOOL gbCapturing = FALSE; +MovieMaker gMovieMaker; + +S32 CHAT_BAR_HEIGHT = 28; +S32 OVERLAY_BAR_HEIGHT = 20; + +const U8 NO_FACE = 255; +BOOL gQuietSnapshot = FALSE; + +const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before coming back +const F32 MAX_FAST_FRAME_TIME = 0.5f; +const F32 FAST_FRAME_INCREMENT = 0.1f; + +const S32 PICK_HALF_WIDTH = 5; +const S32 PICK_DIAMETER = 2 * PICK_HALF_WIDTH+1; + +const F32 MIN_DISPLAY_SCALE = 0.85f; + +#ifdef SABINRIG +/// ALL RIG STUFF +bool rigControl = false; +bool voltDisplay = true; +bool nominalX = false; +bool nominalY = false; +static F32 nomerX = 0.0f; +static F32 nomerY = 0.0f; +const BOARD_NUM = 0; // rig stuff! +const ADRANGE = BIP10VOLTS; // rig stuff! +static unsigned short DataVal; // rig stuff! +static F32 oldValueX = 0; +static F32 newValueX = 50; +static F32 oldValueY = 0; +static F32 newValueY = 50; +static S32 mouseX = 50; +static S32 mouseY = 50; +static float VoltageX = 50; // rig stuff! +static float VoltageY = 50; // rig stuff! +static float nVoltX = 0; +static float nVoltY = 0; +static F32 temp1 = 50.f; +static F32 temp2 = 20.f; +LLCoordGL new_gl; +#endif //SABINRIG + +char LLViewerWindow::sSnapshotBaseName[LL_MAX_PATH]; +char LLViewerWindow::sSnapshotDir[LL_MAX_PATH]; + +char LLViewerWindow::sMovieBaseName[LL_MAX_PATH]; + +extern void toggle_debug_menus(void*); + +#ifdef SABINRIG +// static +void LLViewerWindow::printFeedback() +{ + if(rigControl == true) + { + cbAIn (BOARD_NUM, 0, ADRANGE, &DataVal); + cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageX); //Convert raw to voltage for X-axis + cbAIn (BOARD_NUM, 1, ADRANGE, &DataVal); + cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageY); //Convert raw to voltage for Y-axis + if(voltDisplay == true) + { + llinfos << "Current Voltages - X:" << VoltageX << " Y:" << VoltageY << llendl; //Display voltage + } + + if(nVoltX == 0) + { + nVoltX = VoltageX; + nVoltY = VoltageY; //First time setup of nominal values. + } + + newValueX = VoltageX; + newValueY = VoltageY; //Take in current voltage and set to a separate value for adjustment. + + mouseX = mCurrentMousePoint.mX; + mouseY = mCurrentMousePoint.mY; //Take in current cursor position and set to separate value for adjustment. + + if( abs(newValueX - nVoltX) > nomerX ) + { + if( (newValueX - oldValueX) < 0) + { + mouseX += (S32)( ((newValueX - oldValueX)*.5)) * -temp; + } + else + { + mouseX += (S32)( ((newValueX - oldValueX)*.5) * temp1); + } + } + else + { + mouseX = getWindowWidth() / 2; + } + if( abs(newValueY - nVoltY) > nomerY ) + { + if( (newValueY - oldValueY) < 0) + { + mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * -temp2); + } + else + { + mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * temp2); + } + } + else + { + mouseY = getWindowHeight() / 2; + } + //mouseX += (S32)( (newValueX - nVoltX) * temp1 + 0.5 ); + // (newValueX - oldValueX) = difference between current position and nominal position + // * temp1 = the amplification of the number that sets sensitivity + // + 0.5 = fixes rounding errors + + + //mouseY += (S32)( (newValueY - nVoltY) * temp2 + 0.5 ); //Algorithm to adjust voltage for mouse adjustment. + + oldValueX = newValueX; + oldValueY = newValueY; + + new_gl.mX = mouseX; + new_gl.mY = mouseY; //Setup final coordinate to move mouse to. + + setCursorPosition(new_gl); //Set final cursor position + } +} +#endif //SABINRIG + + +// +// LLViewerWindow +// + +BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + if (gDebugClicks) + { + llinfos << "ViewerWindow left mouse down at " << x << "," << y << llendl; + } + + mLeftMouseDown = TRUE; + + // Make sure we get a coresponding mouseup event, even if the mouse leaves the window + mWindow->captureMouse(); + + // Indicate mouse was active + gMouseIdleTimer.reset(); + + // Hide tooltips on mousedown + if( mToolTip ) + { + mToolTip->setVisible( FALSE ); + } + + // Also hide hover info on mousedown + if (gHoverView) + { + gHoverView->cancelHover(); + } + + if (gToolMgr) + { + // Don't let the user move the mouse out of the window until mouse up. + if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() ) + { + mWindow->setMouseClipping(TRUE); + } + } + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + if (LLView::sDebugMouseHandling) + { + llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl; + } + + return mouse_captor->handleMouseDown(local_x, local_y, mask); + } + + // Topmost view gets a chance before the hierarchy + LLView* top_view = gFocusMgr.getTopView(); + if (top_view) + { + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + if (top_view->pointInView(local_x, local_y) && top_view->handleMouseDown(local_x, local_y, mask)) return TRUE; + } + + // Give the UI views a chance to process the click + if( mRootView->handleMouseDown(x, y, mask) ) + { + if (LLView::sDebugMouseHandling) + { + llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl; + LLView::sMouseHandlerMessage = ""; + } + return TRUE; + } + else if (LLView::sDebugMouseHandling) + { + llinfos << "Left Mouse Down not handled by view" << llendl; + } + + if (gDisconnected) + { + return FALSE; + } + + if (gToolMgr) + { + if(gToolMgr->getCurrentTool(mask)->handleMouseDown( x, y, mask ) ) + { + // This is necessary to force clicks in the world to cause edit + // boxes that might have keyboard focus to relinquish it, and hence + // cause a commit to update their value. JC + gFocusMgr.setKeyboardFocus(NULL, NULL); + return TRUE; + } + } + + return FALSE; +} + +BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + if (gDebugClicks) + { + llinfos << "ViewerWindow left mouse double-click at " << x << "," << y << llendl; + } + + mLeftMouseDown = TRUE; + + // Hide tooltips + if( mToolTip ) + { + mToolTip->setVisible( FALSE ); + } + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + if (LLView::sDebugMouseHandling) + { + llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl; + } + + return mouse_captor->handleDoubleClick(local_x, local_y, mask); + } + + // Check for hit on UI. + LLView* top_view = gFocusMgr.getTopView(); + if (top_view) + { + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + if (top_view->pointInView(local_x, local_y) && top_view->handleDoubleClick(local_x, local_y, mask)) return TRUE; + } + + if (mRootView->handleDoubleClick(x, y, mask)) + { + if (LLView::sDebugMouseHandling) + { + llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl; + LLView::sMouseHandlerMessage = ""; + } + return TRUE; + } + else if (LLView::sDebugMouseHandling) + { + llinfos << "Left Mouse Down not handled by view" << llendl; + } + + // Why is this here? JC 9/3/2002 + if (gNoRender) + { + return TRUE; + } + + if (gToolMgr) + { + if(gToolMgr->getCurrentTool(mask)->handleDoubleClick( x, y, mask ) ) + { + return TRUE; + } + } + + // if we got this far and nothing handled a double click, pass a normal mouse down + return handleMouseDown(window, pos, mask); +} + +BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + if (gDebugClicks) + { + llinfos << "ViewerWindow left mouse up" << llendl; + } + + mLeftMouseDown = FALSE; + + // Indicate mouse was active + gMouseIdleTimer.reset(); + + // Hide tooltips on mouseup + if( mToolTip ) + { + mToolTip->setVisible( FALSE ); + } + + // Also hide hover info on mouseup + if (gHoverView) gHoverView->cancelHover(); + + BOOL handled = FALSE; + + mWindow->releaseMouse(); + + LLTool *tool = NULL; + if (gToolMgr) + { + tool = gToolMgr->getCurrentTool(mask); + + if( tool->clipMouseWhenDown() ) + { + mWindow->setMouseClipping(FALSE); + } + } + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + if (LLView::sDebugMouseHandling) + { + llinfos << "Left Mouse Up handled by captor " << mouse_captor->getName() << llendl; + } + + return mouse_captor->handleMouseUp(local_x, local_y, mask); + } + + LLView* top_view = gFocusMgr.getTopView(); + if (top_view) + { + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + handled = top_view->pointInView(local_x, local_y) && top_view->handleMouseUp(local_x, local_y, mask); + } + + if( !handled ) + { + handled = mRootView->handleMouseUp(x, y, mask); + } + + if (LLView::sDebugMouseHandling) + { + if (handled) + { + llinfos << "Left Mouse Up" << LLView::sMouseHandlerMessage << llendl; + LLView::sMouseHandlerMessage = ""; + } + else + { + llinfos << "Left Mouse Up not handled by view" << llendl; + } + } + + if( !handled ) + { + if (tool) + { + handled = tool->handleMouseUp(x, y, mask); + } + } + + // Always handled as far as the OS is concerned. + return TRUE; +} + + +BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + if (gDebugClicks) + { + llinfos << "ViewerWindow right mouse down at " << x << "," << y << llendl; + } + + mRightMouseDown = TRUE; + + // Make sure we get a coresponding mouseup event, even if the mouse leaves the window + mWindow->captureMouse(); + + // Hide tooltips + if( mToolTip ) + { + mToolTip->setVisible( FALSE ); + } + + // Also hide hover info on mousedown + if (gHoverView) + { + gHoverView->cancelHover(); + } + + if (gToolMgr) + { + // Don't let the user move the mouse out of the window until mouse up. + if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() ) + { + mWindow->setMouseClipping(TRUE); + } + } + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + if (LLView::sDebugMouseHandling) + { + llinfos << "Right Mouse Down handled by captor " << mouse_captor->getName() << llendl; + } + return mouse_captor->handleRightMouseDown(local_x, local_y, mask); + } + + LLView* top_view = gFocusMgr.getTopView(); + if (top_view) + { + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + if (top_view->pointInView(local_x, local_y) && top_view->handleRightMouseDown(local_x, local_y, mask)) return TRUE; + } + + if( mRootView->handleRightMouseDown(x, y, mask) ) + { + if (LLView::sDebugMouseHandling) + { + llinfos << "Right Mouse Down" << LLView::sMouseHandlerMessage << llendl; + LLView::sMouseHandlerMessage = ""; + } + return TRUE; + } + else if (LLView::sDebugMouseHandling) + { + llinfos << "Right Mouse Down not handled by view" << llendl; + } + + if (gToolMgr) + { + if(gToolMgr->getCurrentTool(mask)->handleRightMouseDown( x, y, mask ) ) + { + // This is necessary to force clicks in the world to cause edit + // boxes that might have keyboard focus to relinquish it, and hence + // cause a commit to update their value. JC + gFocusMgr.setKeyboardFocus(NULL, NULL); + return TRUE; + } + } + + //FIXME: this should be rolled into the composite tool logic, not hardcoded at the top level + if (gToolPie && (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode()) ) + { + // If the current tool didn't process the click, we should show + // the pie menu. This can be done by passing the event to the pie + // menu tool. + gToolPie->handleRightMouseDown(x, y, mask); + // show_context_menu( x, y, mask ); + } + + return TRUE; +} + +BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + // Don't care about caps lock for mouse events. + if (gDebugClicks) + { + llinfos << "ViewerWindow right mouse up" << llendl; + } + + mRightMouseDown = FALSE; + + // Indicate mouse was active + gMouseIdleTimer.reset(); + + // Hide tooltips on mouseup + if( mToolTip ) + { + mToolTip->setVisible( FALSE ); + } + + // Also hide hover info on mouseup + if (gHoverView) gHoverView->cancelHover(); + + BOOL handled = FALSE; + + mWindow->releaseMouse(); + + LLTool *tool = NULL; + if (gToolMgr) + { + tool = gToolMgr->getCurrentTool(mask); + + if( tool->clipMouseWhenDown() ) + { + mWindow->setMouseClipping(FALSE); + } + } + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + if (LLView::sDebugMouseHandling) + { + llinfos << "Right Mouse Up handled by captor " << mouse_captor->getName() << llendl; + } + return mouse_captor->handleRightMouseUp(local_x, local_y, mask); + } + + LLView* top_view = gFocusMgr.getTopView(); + if (top_view) + { + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + handled = top_view->pointInView(local_x, local_y) && top_view->handleRightMouseUp(local_x, local_y, mask); + } + + if( !handled ) + { + handled = mRootView->handleRightMouseUp(x, y, mask); + } + + if (LLView::sDebugMouseHandling) + { + if (handled) + { + llinfos << "Right Mouse Up" << LLView::sMouseHandlerMessage << llendl; + LLView::sMouseHandlerMessage = ""; + } + else + { + llinfos << "Right Mouse Up not handled by view" << llendl; + } + } + + if( !handled ) + { + if (tool) + { + handled = tool->handleRightMouseUp(x, y, mask); + } + } + + // Always handled as far as the OS is concerned. + return TRUE; +} + + +void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + mMouseInWindow = TRUE; + + // Save mouse point for access during idle() and display() + + LLCoordGL prev_saved_mouse_point = mCurrentMousePoint; + LLCoordGL mouse_point(x, y); + saveLastMouse(mouse_point); + BOOL mouse_actually_moved = (prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY); + + gMouseIdleTimer.reset(); + + mWindow->showCursorFromMouseMove(); + + if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + + if(mToolTip && mouse_actually_moved) + { + mToolTipBlocked = FALSE; // Blocking starts on keyboard events and (only) ends here. + if( mToolTip->getVisible() && !mToolTipStickyRect.pointInRect( x, y ) ) + { + mToolTip->setVisible( FALSE ); + } + } + + // Activate the hover picker on mouse move. + if (gHoverView) + { + gHoverView->setTyping(FALSE); + } +} + +void LLViewerWindow::handleMouseLeave(LLWindow *window) +{ + // Note: we won't get this if we have captured the mouse. + llassert( gFocusMgr.getMouseCapture() == NULL ); + mMouseInWindow = FALSE; + if (mToolTip) + { + mToolTip->setVisible( FALSE ); + } +} + +BOOL LLViewerWindow::handleCloseRequest(LLWindow *window) +{ + // User has indicated they want to close, but we may need to ask + // about modified documents. + app_request_quit(); + // Don't quit immediately + return FALSE; +} + +void LLViewerWindow::handleQuit(LLWindow *window) +{ + app_force_quit(NULL); +} + +void LLViewerWindow::handleResize(LLWindow *window, S32 width, S32 height) +{ + reshape(width, height); +} + +// The top-level window has gained focus (e.g. via ALT-TAB) +void LLViewerWindow::handleFocus(LLWindow *window) +{ + gFocusMgr.setAppHasFocus(TRUE); + LLModalDialog::onAppFocusGained(); + if (gToolMgr) + { + gToolMgr->onAppFocusGained(); + } + + gShowTextEditCursor = TRUE; + + // See if we're coming in with modifier keys held down + if (gKeyboard) + { + gKeyboard->resetMaskKeys(); + } +} + +// The top-level window has lost focus (e.g. via ALT-TAB) +void LLViewerWindow::handleFocusLost(LLWindow *window) +{ + gFocusMgr.setAppHasFocus(FALSE); + //LLModalDialog::onAppFocusLost(); + if( gToolMgr ) + { + gToolMgr->onAppFocusLost(); + } + gFocusMgr.setMouseCapture( NULL, NULL ); + + // restore mouse cursor + gViewerWindow->showCursor(); + gViewerWindow->getWindow()->setMouseClipping(FALSE); + + // JC - Leave keyboard focus, so if you're popping in and out editing + // a script, you don't have to click in the editor again and again. + // gFocusMgr.setKeyboardFocus( NULL, NULL ); + gShowTextEditCursor = FALSE; + + // If losing focus while keys are down, reset them. + if (gKeyboard) + { + gKeyboard->resetKeys(); + } +} + + +BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) +{ + if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + + // HACK: We want to interpret KEY_RETURN later when it arrives as a Unicode char, + // not as a keydown. Otherwise when client frame rate is really low, hitting + // return sends your chat text before it's all entered/processed. + if (key == KEY_RETURN && mask == MASK_NONE) + { + return FALSE; + } + + return gViewerKeyboard.handleKey(key, mask, repeated); +} + +BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) +{ + return FALSE; +} + + +void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) +{ + return gViewerKeyboard.scanKey(key, key_down, key_up, key_level); +} + + + + +BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated) +{ + if (activated) + { + mActive = TRUE; + send_agent_resume(); + gAgent.clearAFK(); + if (mWindow->getFullscreen() && !mIgnoreActivate) + { + if (!gQuit) + { + if (gStartupState >= STATE_STARTED) + { + // if we're in world, show a progress bar to hide reloading of textures + llinfos << "Restoring GL during activate" << llendl; + restoreGL("Restoring..."); + } + else + { + // otherwise restore immediately + restoreGL(); + } + } + else + { + llwarns << "Activating while quitting" << llendl; + } + } + + // Unmute audio + if (!gSavedSettings.getBOOL("MuteAudio")) + { + if (gAudiop) gAudiop->setMuted(FALSE); + F32 volume = gSavedSettings.getF32("MediaAudioVolume"); + if(LLMediaEngine::getInstance()) + { + LLMediaEngine::getInstance()->setVolume(volume); + LLMediaEngine::updateClass(volume); + } + } + } + else + { + mActive = FALSE; + gAgent.setAFK(); + send_agent_pause(); + if (mWindow->getFullscreen() && !mIgnoreActivate) + { + llinfos << "Stopping GL during deactivation" << llendl; + stopGL(); + } + // Mute audio + if (gSavedSettings.getBOOL("MuteWhenMinimized")) + { + llinfos << "Muting audio on minimize" << llendl; + if (gAudiop) gAudiop->setMuted(TRUE); + F32 volume = 0.f; + LLMediaEngine::getInstance()->setVolume(volume); + LLMediaEngine::updateClass(volume); + } + } + return TRUE; +} + + +void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item) +{ +} + + +BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height) +{ +#if LL_WINDOWS + if (gNoRender) + { + HWND window_handle = llwindow_get_hwnd(window); + PAINTSTRUCT ps; + HDC hdc; + + RECT wnd_rect; + wnd_rect.left = 0; + wnd_rect.top = 0; + wnd_rect.bottom = 200; + wnd_rect.right = 500; + + hdc = BeginPaint(window_handle, &ps); + //SetBKColor(hdc, RGB(255, 255, 255)); + FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255))); + + LLString name_str; + gAgent.getName(name_str); + + S32 len; + char temp_str[255]; + sprintf(temp_str, "%s FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", + name_str.c_str(), + gViewerStats->mFPSStat.getMeanPerSec(), + gViewerStats->mSimPhysicsFPS.getPrev(0), + gViewerStats->mSimTimeDilation.getPrev(0)); + len = strlen(temp_str); + TextOutA(hdc, 0, 0, temp_str, len); + + + LLVector3d pos_global = gAgent.getPositionGlobal(); + sprintf(temp_str, "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]); + len = strlen(temp_str); + TextOutA(hdc, 0, 25, temp_str, len); + + TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61); + EndPaint(window_handle, &ps); + return TRUE; + } +#endif + return FALSE; +} + + +void LLViewerWindow::handleScrollWheel(LLWindow *window, S32 clicks) +{ + gViewerWindow->handleScrollWheel( clicks ); +} + +void LLViewerWindow::handleWindowBlock(LLWindow *window) +{ + send_agent_pause(); +} + +void LLViewerWindow::handleWindowUnblock(LLWindow *window) +{ + send_agent_resume(); +} + +void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data) +{ + switch (data_type) + { + case 0: + // received URL + if (LLURLSimString::unpack_data(data)) + { + if(gFloaterWorldMap) + { + gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName, + LLURLSimString::sInstance.mX, + LLURLSimString::sInstance.mY, + LLURLSimString::sInstance.mZ); + + LLFloaterWorldMap::show(NULL, TRUE); + } + + // bring window to foreground, as it has just been "launched" from a URL + mWindow->bringToFront(); + } + break; + } +} + + +// +// Classes +// +LLViewerWindow::LLViewerWindow( + char* title, char* name, + S32 x, S32 y, + S32 width, S32 height, + BOOL fullscreen, BOOL ignore_pixel_depth) + : + mActive(TRUE), + mWantFullscreen(fullscreen), + mShowFullscreenProgress(FALSE), + mWindowRect(0, height, width, 0), + mVirtualWindowRect(0, height, width, 0), + mLeftMouseDown(FALSE), + mRightMouseDown(FALSE), + mToolTip(NULL), + mToolTipBlocked(FALSE), + mMouseInWindow( FALSE ), + mLastMask( MASK_NONE ), + mToolStored( NULL ), + mSuppressToolbox( FALSE ), + mHideCursorPermanent( FALSE ), + mPickPending(FALSE), + mIgnoreActivate( FALSE ), + mRenderFullFrame(FALSE) +{ + // Default to application directory. + strcpy(LLViewerWindow::sSnapshotBaseName, "Snapshot"); + strcpy(LLViewerWindow::sMovieBaseName, "SLmovie"); + LLViewerWindow::sSnapshotDir[0] = '\0'; + + mFastFrameTimer.stop(); + + // create window + mWindow = LLWindowManager::createWindow( + title, name, x, y, width, height, 0, + fullscreen, + gNoRender, + gSavedSettings.getBOOL("DisableVerticalSync"), + !gNoRender, + ignore_pixel_depth); +#if LL_WINDOWS + if (!LLWinDebug::setupExceptionHandler()) + { + llwarns << " Someone took over my exception handler (post createWindow)!" << llendl; + } +#endif + + if (NULL == mWindow) + { + LLSplashScreen::update("Shutting down..."); +#if LL_LINUX + llwarns << "Unable to create window, be sure screen is set at 32-bit color and your graphics driver is configured correctly. See README-linux.txt for further information." + << llendl; +#else + llwarns << "Unable to create window, be sure screen is set at 32-bit color in Control Panels->Display->Settings" + << llendl; +#endif + app_force_exit(1); + } + + // Get the real window rect the window was created with (since there are various OS-dependent reasons why + // the size of a window or fullscreen context may have been adjusted slightly...) + F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); + + mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); + mDisplayScale *= ui_scale_factor; + LLUI::setScaleFactor(mDisplayScale); + + { + LLCoordWindow size; + mWindow->getSize(&size); + mWindowRect.set(0, size.mY, size.mX, 0); + mVirtualWindowRect.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0); + } + + LLFontManager::initClass(); + + // + // We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off + // stuff like AGP if we think that it'll crash the viewer. + // + gFeatureManagerp->initGraphicsFeatureMasks(); + if (gFeatureManagerp->isSafe() + || (gSavedSettings.getS32("LastFeatureVersion") != gFeatureManagerp->getVersion())) + { + gFeatureManagerp->applyRecommendedFeatures(); + } + + S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting"); + // -1 indicates use default (max) + if (idx == -1) + { + idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting + gSavedSettings.setS32("GraphicsCardMemorySetting", idx); + } + + if (!gNoRender) + { + // + // Initialize GL stuff + // + + gPipeline.init(); + stop_glerror(); + initGLDefaults(); + LLViewerImage::initClass(); + } + + // + // Done initing GL stuff. + // + + // set callbacks + mWindow->setCallbacks(this); + + // Init the image list. Must happen after GL is initialized and before the images that + // LLViewerWindow needs are requested. + gImageList.init(); + gBumpImageList.init(); + + // Create container for all sub-views + mRootView = new LLRootView("root", mVirtualWindowRect, FALSE); + mRootView->setRenderInFastFrame(FALSE); + + if (!gNoRender) + { + // Init default fonts + initFonts(); + } + + // Init Resource Manager + gResMgr = new LLResMgr(); + + // Make avatar head look forward at start + mCurrentMousePoint.mX = getWindowWidth() / 2; + mCurrentMousePoint.mY = getWindowHeight() / 2; + + mPickBuffer = new U8[PICK_DIAMETER * PICK_DIAMETER * 4]; + + gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle"); + mOverlayTitle = gSavedSettings.getString("OverlayTitle"); + // Can't have spaces in settings.ini strings, so use underscores instead and convert them. + LLString::replaceChar(mOverlayTitle, '_', ' '); + + gAwayTimer.stop(); + + LLAlertDialog::setDisplayCallback(alertCallback); // call this before calling any modal dialogs + + // sync the keyboard's setting with the saved setting + gSavedSettings.getControl("NumpadControl")->firePropertyChanged(); +} + +void LLViewerWindow::initGLDefaults() +{ + //LLGLState::reset(); + //gGLSDefault.set(); + //LLGLState::verify(TRUE); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); + + F32 ambient[4] = {0.f,0.f,0.f,0.f }; + F32 diffuse[4] = {1.f,1.f,1.f,1.f }; + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient); + glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse); + + glPixelStorei(GL_PACK_ALIGNMENT,1); + glPixelStorei(GL_UNPACK_ALIGNMENT,1); + + // lights for objects + glShadeModel( GL_SMOOTH ); + + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); + + glCullFace(GL_BACK); + + // RN: Need this for translation and stretch manip. + gCone.prerender(); + gBox.prerender(); + gSphere.prerender(); + gCylinder.prerender(); + + LLVOAvatar::initVertexPrograms(); +} + +void LLViewerWindow::initBase() +{ + S32 height = getWindowHeight(); + S32 width = getWindowWidth(); + + LLRect full_window(0, height, width, 0); + + adjustRectanglesForFirstUse(full_window); + + //////////////////// + // + // Set the gamma + // + + F32 gamma = gSavedSettings.getF32("RenderGamma"); + if (gamma != 0.0f) + { + gViewerWindow->getWindow()->setGamma(gamma); + } + + // Create global views + + // Create the floater view at the start so that other views can add children to it. + // (But wait to add it as a child of the root view so that it will be in front of the + // other views.) + + // Constrain floaters to inside the menu and status bar regions. + LLRect floater_view_rect = full_window; + // make space for menu bar if we have one + floater_view_rect.mTop -= MENU_BAR_HEIGHT; + floater_view_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 2; + + // Check for non-first startup + S32 floater_view_bottom = gSavedSettings.getS32("FloaterViewBottom"); + if (floater_view_bottom >= 0) + { + floater_view_rect.mBottom = floater_view_bottom; + } + gFloaterView = new LLFloaterView("Floater View", floater_view_rect ); + gFloaterView->setVisible(TRUE); + + gSnapshotFloaterView = new LLSnapshotFloaterView("Snapshot Floater View", full_window); + gSnapshotFloaterView->setVisible(TRUE); + + // Console + llassert( !gConsole ); + LLRect console_rect = full_window; + console_rect.mTop -= 24; + console_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 12; + console_rect.mLeft += 24; //gSavedSettings.getS32("StatusBarButtonWidth") + gSavedSettings.getS32("StatusBarPad"); + + if (gSavedSettings.getBOOL("ChatFullWidth")) + { + console_rect.mRight -= 10; + } + else + { + // Make console rect somewhat narrow so having inventory open is + // less of a problem. + console_rect.mRight = console_rect.mLeft + 2 * width / 3; + } + + gConsole = new LLConsole( + "console", + gSavedSettings.getS32("ConsoleBufferSize"), + console_rect, + gSavedSettings.getS32("ChatFontSize"), + gSavedSettings.getF32("ChatPersistTime") ); + gConsole->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); + mRootView->addChild(gConsole); + + // Debug view over the console + gDebugView = new LLDebugView("gDebugView", full_window); + gDebugView->setFollowsAll(); + gDebugView->setVisible(TRUE); + mRootView->addChild(gDebugView); + + // HUD elements just below floaters + LLRect hud_rect = full_window; + hud_rect.mTop -= 24; + hud_rect.mBottom += STATUS_BAR_HEIGHT; + gHUDView = new LLHUDView("hud_view", hud_rect); + gHUDView->setFollowsAll(); + mRootView->addChild(gHUDView); + + // Add floater view at the end so it will be on top, and give it tab priority over others + mRootView->addChild(gFloaterView, -1); + mRootView->addChild(gSnapshotFloaterView); + + // notify above floaters! + LLRect notify_rect = full_window; + //notify_rect.mTop -= 24; + notify_rect.mBottom += STATUS_BAR_HEIGHT; + gNotifyBoxView = new LLNotifyBoxView("notify", notify_rect, FALSE, FOLLOWS_ALL); + mRootView->addChild(gNotifyBoxView, -2); + + // Tooltips go above floaters + mToolTip = new LLTextBox( "tool tip", LLRect(0, 1, 1, 0 ) ); + mToolTip->setHPad( 4 ); + mToolTip->setVPad( 2 ); + mToolTip->setColor( gColors.getColor( "ToolTipTextColor" ) ); + mToolTip->setBorderColor( gColors.getColor( "ToolTipBorderColor" ) ); + mToolTip->setBorderVisible( FALSE ); + mToolTip->setBackgroundColor( gColors.getColor( "ToolTipBgColor" ) ); + mToolTip->setBackgroundVisible( TRUE ); + mToolTip->setDropshadowVisible( FALSE ); + mToolTip->setBorderDropshadowVisible( TRUE ); + mToolTip->setVisible( FALSE ); + + // Add the progress bar view (startup view), which overrides everything + mProgressView = new LLProgressView("ProgressView", full_window); + mRootView->addChild(mProgressView); + setShowProgress(FALSE); + setProgressCancelButtonVisible(FALSE, ""); +} + + +void adjust_rect_top_left(const LLString& control, const LLRect& window) +{ + LLRect r = gSavedSettings.getRect(control); + if (r.mLeft == 0 && r.mBottom == 0) + { + r.setLeftTopAndSize(0, window.getHeight(), r.getWidth(), r.getHeight()); + gSavedSettings.setRect(control, r); + } +} + +void adjust_rect_top_right(const LLString& control, const LLRect& window) +{ + LLRect r = gSavedSettings.getRect(control); + if (r.mLeft == 0 && r.mBottom == 0) + { + r.setLeftTopAndSize(window.getWidth() - r.getWidth(), + window.getHeight(), + r.getWidth(), + r.getHeight()); + gSavedSettings.setRect(control, r); + } +} + +void adjust_rect_bottom_center(const LLString& control, const LLRect& window) +{ + LLRect r = gSavedSettings.getRect(control); + if (r.mLeft == 0 && r.mBottom == 0) + { + r.setOriginAndSize( + window.getWidth()/2 - r.getWidth()/2, + 0, + r.getWidth(), + r.getHeight()); + gSavedSettings.setRect(control, r); + } +} + +// Many rectangles can't be placed until we know the screen size. +// These rectangles have their bottom-left corner as 0,0 +void LLViewerWindow::adjustRectanglesForFirstUse(const LLRect& window) +{ + LLRect r; + + adjust_rect_bottom_center("FloaterMoveRect", window); + + adjust_rect_bottom_center("FloaterCameraRect", window); + + adjust_rect_top_left("FloaterCustomizeAppearanceRect", window); + + adjust_rect_top_left("FloaterLandRect5", window); + + adjust_rect_top_left("FloaterFindRect2", window); + + adjust_rect_top_left("FloaterGestureRect", window); + + adjust_rect_top_right("FloaterMapRect", window); + + adjust_rect_top_left("FloaterBuildOptionsRect", window); + + // bottom-right + r = gSavedSettings.getRect("FloaterInventoryRect"); + if (r.mLeft == 0 && r.mBottom == 0) + { + r.setOriginAndSize( + window.getWidth() - r.getWidth(), + 0, + r.getWidth(), + r.getHeight()); + gSavedSettings.setRect("FloaterInventoryRect", r); + } +} + + +void LLViewerWindow::initWorldUI() +{ + pre_init_menus(); + + S32 height = mRootView->getRect().getHeight(); + S32 width = mRootView->getRect().getWidth(); + LLRect full_window(0, height, width, 0); + + LLRect bar_rect(-1, STATUS_BAR_HEIGHT, width+1, -1); + gToolBar = new LLToolBar("toolbar", bar_rect); + + LLRect chat_bar_rect(-1,CHAT_BAR_HEIGHT, width+1, -1); + chat_bar_rect.translate(0, STATUS_BAR_HEIGHT-1); + gChatBar = new LLChatBar("chat", chat_bar_rect); + + bar_rect.translate(0, STATUS_BAR_HEIGHT-1); + bar_rect.translate(0, CHAT_BAR_HEIGHT-1); + gOverlayBar = new LLOverlayBar("overlay", bar_rect); + + // panel containing chatbar, toolbar, and overlay, over floaters + LLRect bottom_rect(-1, 2*STATUS_BAR_HEIGHT + CHAT_BAR_HEIGHT, width+1, -1); + gBottomPanel = new LLBottomPanel("bottom panel", bottom_rect); + + // the order here is important + gBottomPanel->addChild(gChatBar); + gBottomPanel->addChild(gToolBar); + gBottomPanel->addChild(gOverlayBar); + mRootView->addChild(gBottomPanel); + + // View for hover information + gHoverView = new LLHoverView("gHoverView", full_window); + gHoverView->setVisible(TRUE); + mRootView->addChild(gHoverView); + + // + // Map + // + // TODO: Move instance management into class + gFloaterMap = new LLFloaterMap("Map"); + gFloaterMap->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT); + gFloaterMap->setVisible( gSavedSettings.getBOOL("ShowMiniMap") ); + + // keep onscreen + gFloaterView->adjustToFitScreen(gFloaterMap, FALSE); + + if (gSavedSettings.getBOOL("ShowCameraControls")) + { + LLFloaterCamera::show(NULL); + } + + if (gSavedSettings.getBOOL("ShowMovementControls")) + { + LLFloaterMove::show(NULL); + } + + // Must have one global chat floater so it can actually store + // the history. JC + gFloaterChat = new LLFloaterChat(); + gFloaterChat->setVisible( FALSE ); + + if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) gFloaterChat->loadHistory(); + + gIMView = new LLIMView("gIMView", LLRect() ); + gIMView->setFollowsAll(); + + LLRect morph_view_rect = full_window; + morph_view_rect.stretch( -STATUS_BAR_HEIGHT ); + morph_view_rect.mTop = full_window.mTop - 32; + gMorphView = new LLMorphView("gMorphView", morph_view_rect ); + mRootView->addChild(gMorphView); + gMorphView->setVisible(FALSE); + + gFloaterMute = new LLFloaterMute(); + gFloaterMute->setVisible(FALSE); + + LLWorldMapView::initClass(); + + LLRect world_map_rect = gSavedSettings.getRect("FloaterWorldMapRect"); + // if 0,0,0,0 then use fullscreen + if (world_map_rect.mTop == 0 + && world_map_rect.mLeft == 0 + && world_map_rect.mRight == 0 + && world_map_rect.mBottom == 0) + { + world_map_rect.set(0, height-TOOL_BAR_HEIGHT, width, STATUS_BAR_HEIGHT); + world_map_rect.stretch(-4); + gSavedSettings.setRect("FloaterWorldMapRect", world_map_rect); + } + gFloaterWorldMap = new LLFloaterWorldMap(); + gFloaterWorldMap->setVisible(FALSE); + + // + // Tools for building + // + + // Toolbox floater + init_menus(); + + gFloaterTools = new LLFloaterTools(); + gFloaterTools->setVisible(FALSE); + + // Status bar + //FIXME change this back + S32 menu_bar_height = gMenuBarView->getRect().getHeight(); + LLRect root_rect = gViewerWindow->getRootView()->getRect(); + LLRect status_rect(0, root_rect.getHeight(), root_rect.getWidth(), root_rect.getHeight() - menu_bar_height); + gStatusBar = new LLStatusBar("status", status_rect); + gStatusBar->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP); + + gStatusBar->reshape(root_rect.getWidth(), gStatusBar->getRect().getHeight(), TRUE); + gStatusBar->translate(0, root_rect.getHeight() - gStatusBar->getRect().getHeight()); + + gViewerWindow->getRootView()->addChild(gStatusBar); +} + + +LLViewerWindow::~LLViewerWindow() +{ + gSavedSettings.setS32("FloaterViewBottom", gFloaterView->getRect().mBottom); + + // Cleanup global views + if (gMorphView) + { + gMorphView->setVisible(FALSE); + } + + // Delete all child views. + delete mRootView; + mRootView = NULL; + + // Automatically deleted as children of mRootView. Fix the globals. + gFloaterTools = NULL; + gStatusBar = NULL; + gFloaterCamera = NULL; + gIMView = NULL; + gHoverView = NULL; + + gFloaterView = NULL; + gMorphView = NULL; + + gFloaterChat = NULL; + gFloaterMute = NULL; + + gFloaterMap = NULL; + gHUDView = NULL; + + gNotifyBoxView = NULL; + + delete mToolTip; + mToolTip = NULL; + + delete gResMgr; + gResMgr = NULL; + + //-------------------------------------------------------- + // Shutdown GL cleanly. Order is very important here. + //-------------------------------------------------------- + LLFontGL::destroyDefaultFonts(); + LLFontManager::cleanupClass(); + stop_glerror(); + + gSky.cleanup(); + stop_glerror(); + + gImageList.shutdown(); + stop_glerror(); + + gBumpImageList.shutdown(); + stop_glerror(); + + LLWorldMapView::cleanupTextures(); + + LLViewerImage::cleanupClass(); + + delete[] mPickBuffer; + mPickBuffer = NULL; + + llinfos << "Stopping GL during shutdown" << llendl; + if (!gNoRender) + { + stopGL(FALSE); + stop_glerror(); + } + + if (gSelectMgr) + { + llinfos << "Cleaning up select manager" << llendl; + gSelectMgr->cleanup(); + } + + llinfos << "Cleaning up pipeline" << llendl; + gPipeline.cleanup(); + stop_glerror(); + llinfos << "Destroying Window" << llendl; + destroyWindow(); +} + + +void LLViewerWindow::setCursor( ECursorType c ) +{ + mWindow->setCursor( c ); +} + +void LLViewerWindow::showCursor() +{ + mWindow->showCursor(); +} + +void LLViewerWindow::hideCursor() +{ + // Hide tooltips + if(mToolTip ) mToolTip->setVisible( FALSE ); + + // Also hide hover info + if (gHoverView) gHoverView->cancelHover(); + + // And hide the cursor + mWindow->hideCursor(); +} + +void LLViewerWindow::sendShapeToSim() +{ + LLMessageSystem* msg = gMessageSystem; + if(!msg) return; + msg->newMessageFast(_PREHASH_AgentHeightWidth); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode); + msg->nextBlockFast(_PREHASH_HeightWidthBlock); + msg->addU32Fast(_PREHASH_GenCounter, 0); + U16 height16 = (U16) mWindowRect.getHeight(); + U16 width16 = (U16) mWindowRect.getWidth(); + msg->addU16Fast(_PREHASH_Height, height16); + msg->addU16Fast(_PREHASH_Width, width16); + gAgent.sendReliableMessage(); +} + +// Must be called after window is created to set up agent +// camera variables and UI variables. +void LLViewerWindow::reshape(S32 width, S32 height) +{ + // Destroying the window at quit time generates spurious + // reshape messages. We don't care about these, and we + // don't want to send messages because the message system + // may have been destructed. + if (!gQuit) + { + if (gNoRender) + { + return; + } + + glViewport(0, 0, width, height ); + + if (height > 0 && gCamera) + { + gCamera->setViewHeightInPixels( height ); + if (mWindow->getFullscreen()) + { + // force to 4:3 aspect for odd resolutions + gCamera->setAspect( getDisplayAspectRatio() ); + } + else + { + gCamera->setAspect( width / (F32) height); + } + } + + // update our window rectangle + mWindowRect.mRight = mWindowRect.mLeft + width; + mWindowRect.mTop = mWindowRect.mBottom + height; + calcDisplayScale(); + + BOOL display_scale_changed = mDisplayScale != LLUI::sGLScaleFactor; + LLUI::setScaleFactor(mDisplayScale); + + // update our window rectangle + mVirtualWindowRect.mRight = mVirtualWindowRect.mLeft + llround((F32)width / mDisplayScale.mV[VX]); + mVirtualWindowRect.mTop = mVirtualWindowRect.mBottom + llround((F32)height / mDisplayScale.mV[VY]); + + setupViewport(); + + // Inform lower views of the change + // round up when converting coordinates to make sure there are no gaps at edge of window + LLView::sForceReshape = display_scale_changed; + mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY])); + LLView::sForceReshape = FALSE; + + // clear font width caches + if (display_scale_changed) + { + LLHUDText::reshape(); + } + + sendShapeToSim(); + + + // store the mode the user wants (even if not there yet) + gSavedSettings.setBOOL("FullScreen", mWantFullscreen); + + // store new settings for the mode we are in, regardless + if (mWindow->getFullscreen()) + { + gSavedSettings.setS32("FullScreenWidth", width); + gSavedSettings.setS32("FullScreenHeight", height); + } + else + { + // Only save size if not maximized + BOOL maximized = mWindow->getMaximized(); + gSavedSettings.setBOOL("WindowMaximized", maximized); + + LLCoordScreen window_size; + if (!maximized + && mWindow->getSize(&window_size)) + { + gSavedSettings.setS32("WindowWidth", window_size.mX); + gSavedSettings.setS32("WindowHeight", window_size.mY); + } + } + + gViewerStats->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width); + gViewerStats->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height); + } +} + + +void LLViewerWindow::draw() +{ +#if LL_DEBUG + LLView::sIsDrawing = TRUE; +#endif + stop_glerror(); + + LLUI::setLineWidth(1.f); + //popup alerts from the UI + LLAlertInfo alert; + while (LLPanel::nextAlert(alert)) + { + alertXml(alert.mLabel, alert.mArgs); + } + + LLUI::setLineWidth(1.f); + // Reset any left-over transforms + glMatrixMode(GL_MODELVIEW); + + glLoadIdentity(); + + //S32 screen_x, screen_y; + + // HACK for timecode debugging + if (gSavedSettings.getBOOL("DisplayTimecode")) + { + // draw timecode block + char text[256]; + + glLoadIdentity(); + + microsecondsToTimecodeString(gFrameTime,text); + const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF ); + font->renderUTF8(text, 0, + llround((getWindowWidth()/2)-100.f), + llround((getWindowHeight()-60.f)), + LLColor4( 1.f, 1.f, 1.f, 1.f ), + LLFontGL::LEFT, LLFontGL::TOP); + } + + // Draw all nested UI views. + // No translation needed, this view is glued to 0,0 + + glPushMatrix(); + { + // scale view by UI global scale factor and aspect ratio correction factor + glScalef(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f); + + LLVector2 old_scale_factor = LLUI::sGLScaleFactor; + if (gCamera) + { + // apply camera zoom transform (for high res screenshots) + F32 zoom_factor = gCamera->getZoomFactor(); + S16 sub_region = gCamera->getZoomSubRegion(); + if (zoom_factor > 1.f) + { + //decompose subregion number to x and y values + int pos_y = sub_region / llceil(zoom_factor); + int pos_x = sub_region - (pos_y*llceil(zoom_factor)); + // offset for this tile + glTranslatef((F32)gViewerWindow->getWindowWidth() * -(F32)pos_x, + (F32)gViewerWindow->getWindowHeight() * -(F32)pos_y, + 0.f); + glScalef(zoom_factor, zoom_factor, 1.f); + LLUI::sGLScaleFactor *= zoom_factor; + } + } + + { + LLGLSTexture gls_texture; + show_text_gl(); + } + + if (gToolMgr) + { + // Draw tool specific overlay on world + gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) )->draw(); + } + + if( gAgent.cameraMouselook() ) + { + drawMouselookInstructions(); + stop_glerror(); + } + + // Draw all nested UI views. + // No translation needed, this view is glued to 0,0 + mRootView->draw(); + + // Draw optional on-top-of-everyone view + LLView* top_view = gFocusMgr.getTopView(); + if (top_view && top_view->getVisible()) + { + S32 screen_x, screen_y; + top_view->localPointToScreen(0, 0, &screen_x, &screen_y); + + glMatrixMode(GL_MODELVIEW); + LLUI::pushMatrix(); + LLUI::translate( (F32) screen_x, (F32) screen_y, 0.f); + top_view->draw(); + LLUI::popMatrix(); + } + + // Draw tooltips + // Adjust their rectangle so they don't go off the top or bottom + // of the screen. + if( mToolTip && mToolTip->getVisible() ) + { + glMatrixMode(GL_MODELVIEW); + LLUI::pushMatrix(); + { + S32 tip_height = mToolTip->getRect().getHeight(); + + S32 screen_x, screen_y; + mToolTip->localPointToScreen(0, -24 - tip_height, + &screen_x, &screen_y); + + // If tooltip would draw off the bottom of the screen, + // show it from the cursor tip position. + if (screen_y < tip_height) + { + mToolTip->localPointToScreen(0, 0, &screen_x, &screen_y); + } + LLUI::translate( (F32) screen_x, (F32) screen_y, 0); + mToolTip->draw(); + } + LLUI::popMatrix(); + } + + if( gShowOverlayTitle && !mOverlayTitle.empty() ) + { + // Used for special titles such as "Second Life - Special E3 2003 Beta" + const S32 DIST_FROM_TOP = 20; + LLGLSTexture gls_texture; + LLFontGL::sSansSerifBig->renderUTF8( + mOverlayTitle, 0, + llround( gViewerWindow->getWindowWidth() * 0.5f), + gViewerWindow->getWindowHeight() - DIST_FROM_TOP, + LLColor4(1, 1, 1, 0.4f), + LLFontGL::HCENTER, LLFontGL::TOP); + } + + LLUI::sGLScaleFactor = old_scale_factor; + } + glPopMatrix(); + + +#if LL_DEBUG + LLView::sIsDrawing = FALSE; +#endif +} + +// Takes a single keydown event, usually when UI is visible +BOOL LLViewerWindow::handleKey(KEY key, MASK mask) +{ + if (gFocusMgr.getKeyboardFocus() && !(mask & (MASK_CONTROL | MASK_ALT))) + { + // We have keyboard focus, and it's not an accelerator + + if (key < 0x80) + { + // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. + return gFocusMgr.childHasKeyboardFocus(mRootView); + } + } + + // HACK look for UI editing keys + if (LLView::sEditingUI) + { + if (LLFloaterEditUI::handleKey(key, mask)) + { + return TRUE; + } + } + + // Hide tooltips on keypress + if(mToolTip ) + { + mToolTipBlocked = TRUE; // block until next time mouse is moved + mToolTip->setVisible( FALSE ); + } + + // Also hide hover info on keypress + if (gHoverView) + { + gHoverView->cancelHover(); + + gHoverView->setTyping(TRUE); + } + + // Explicit hack for debug menu. + if ((MASK_ALT & mask) && + (MASK_CONTROL & mask) && + ('D' == key || 'd' == key)) + { + toggle_debug_menus(NULL); + } + + // Example "bug" for bug reporter web page + if ((MASK_SHIFT & mask) + && (MASK_ALT & mask) + && (MASK_CONTROL & mask) + && ('H' == key || 'h' == key)) + { + trigger_hippo_bug(NULL); + } + + // handle escape key + if (key == KEY_ESCAPE && mask == MASK_NONE) + { + if (gMenuHolder && gMenuHolder->hideMenus()) + { + return TRUE; + } + //FIXME: get this to play well with mouselook and hidden cursor modes, etc. + //if (gFocusMgr.getMouseCapture()) + //{ + // gFocusMgr.setMouseCapture(NULL, NULL); + // return TRUE; + //} + } + + // let menus handle navigation keys + if (gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE)) + { + return TRUE; + } + + // Traverses up the hierarchy + LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus(); + if( keyboard_focus ) + { + // arrow keys move avatar while chatting hack + if (gChatBar && gChatBar->inputEditorHasFocus()) + { + if (gChatBar->getCurrentChat().empty() || gSavedSettings.getBOOL("ArrowKeysMoveAvatar")) + { + switch(key) + { + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + case KEY_PAGE_UP: + case KEY_PAGE_DOWN: + case KEY_HOME: + // when chatbar is empty or ArrowKeysMoveAvatar set, pass arrow keys on to avatar... + return FALSE; + default: + break; + } + } + } + + if (keyboard_focus->handleKey(key, mask, FALSE)) + { + return TRUE; + } + } + + if (gToolMgr) + { + if( gToolMgr->getCurrentTool(mask)->handleKey(key, mask) ) + { + return TRUE; + } + } + + // Try for a new-format gesture + if (gGestureManager.triggerGesture(key, mask)) + { + return TRUE; + } + + // See if this is a gesture trigger. If so, eat the key and + // don't pass it down to the menus. + if (gGestureList.trigger(key, mask)) + { + return TRUE; + } + + // Topmost view gets a chance before the hierarchy + //FIXME: get rid of this? + LLView* top_view = gFocusMgr.getTopView(); + if (top_view) + { + if( top_view->handleKey( key, mask, TRUE ) ) + { + return TRUE; + } + } + + // give floaters first chance to handle TAB key + // so frontmost floater gets focus + if (key == KEY_TAB) + { + // if nothing has focus, go to first or last UI element as appropriate + if (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL) + { + if (gMenuHolder) gMenuHolder->hideMenus(); + + // if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode + gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0); + + // do CTRL-TAB and CTRL-SHIFT-TAB logic + if (mask & MASK_SHIFT) + { + mRootView->focusPrevRoot(); + } + else + { + mRootView->focusNextRoot(); + } + return TRUE; + } + } + + // give menus a chance to handle keys + if (gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) + { + return TRUE; + } + + // don't pass keys on to world when something in ui has focus + return gFocusMgr.childHasKeyboardFocus(mRootView); +} + + +BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) +{ + // HACK: We delay processing of return keys until they arrive as a Unicode char, + // so that if you're typing chat text at low frame rate, we don't send the chat + // until all keystrokes have been entered. JC + // HACK: Numeric keypad <enter> on Mac is Unicode 3 + // HACK: Control-M on Windows is Unicode 13 + if ((uni_char == 13 && mask != MASK_CONTROL) + || (uni_char == 3 && mask == MASK_NONE)) + { + return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); + } + + // Traverses up the hierarchy + LLView* keyboard_focus = gFocusMgr.getKeyboardFocus(); + if( keyboard_focus ) + { + if (keyboard_focus->handleUnicodeChar(uni_char, FALSE)) + { + return TRUE; + } + + // Topmost view gets a chance before the hierarchy + LLView* top_view = gFocusMgr.getTopView(); + if (top_view && top_view->handleUnicodeChar( uni_char, FALSE ) ) + { + return TRUE; + } + + return TRUE; + } + + return FALSE; +} + + +void LLViewerWindow::handleScrollWheel(S32 clicks) +{ + gMouseIdleTimer.reset(); + + // Hide tooltips + if( mToolTip ) + { + mToolTip->setVisible( FALSE ); + } + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y ); + mouse_captor->handleScrollWheel(local_x, local_y, clicks); + if (LLView::sDebugMouseHandling) + { + llinfos << "Scroll Wheel handled by captor " << mouse_captor->getName() << llendl; + } + return; + } + + LLView* top_view = gFocusMgr.getTopView(); + if (top_view) + { + S32 local_x; + S32 local_y; + top_view->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y ); + if (top_view->handleScrollWheel(local_x, local_y, clicks)) return; + } + + if (mRootView->handleScrollWheel(mCurrentMousePoint.mX, mCurrentMousePoint.mY, clicks) ) + { + if (LLView::sDebugMouseHandling) + { + llinfos << "Scroll Wheel" << LLView::sMouseHandlerMessage << llendl; + LLView::sMouseHandlerMessage = ""; + } + return; + } + else if (LLView::sDebugMouseHandling) + { + llinfos << "Scroll Wheel not handled by view" << llendl; + } + + if (gWorldPointer) + { + // Zoom the camera in and out behavior + gAgent.handleScrollWheel(clicks); + } + + return; +} + +void LLViewerWindow::moveCursorToCenter() +{ +#if 1 // old version + +#if 0 // Dave's changes - this reportedly is making the drift worse on some systems? + S32 x = llround((F32) mVirtualWindowRect.getWidth() / 2); + S32 y = llround((F32) mVirtualWindowRect.getHeight() / 2); +#else + S32 x = mVirtualWindowRect.getWidth() / 2; + S32 y = mVirtualWindowRect.getHeight() / 2; +#endif + + //on a forced move, all deltas get zeroed out to prevent jumping + mCurrentMousePoint.set(x,y); + mLastMousePoint.set(x,y); + mCurrentMouseDelta.set(0,0); + + LLUI::setCursorPositionScreen(x, y); + +#else // Richard's version - fails on intel macs + + S32 x = llround((F32) mWindowRect.getWidth() / 2); + S32 y = llround((F32) mWindowRect.getHeight() / 2); + + LLCoordWindow window_point; + mWindow->convertCoords(LLCoordGL(x, y), &window_point); + mWindow->setCursorPosition(window_point); + + // read back cursor position + mWindow->getCursorPosition(&window_point); + LLCoordGL new_mouse_pos; + mWindow->convertCoords(window_point, &new_mouse_pos); + new_mouse_pos.mX = llround((F32)new_mouse_pos.mX / mDisplayScale.mV[VX]); + new_mouse_pos.mY = llround((F32)new_mouse_pos.mY / mDisplayScale.mV[VY]); + + //on a forced move, all deltas get zeroed out to prevent jumping + mCurrentMousePoint = new_mouse_pos; + mLastMousePoint = new_mouse_pos; + mCurrentMouseDelta.set(0,0); +#endif +} + +////////////////////////////////////////////////////////////////////// +// +// Hover handlers +// + +// Update UI based on stored mouse position from mouse-move +// event processing. +BOOL LLViewerWindow::handlePerFrameHover() +{ + static std::string last_handle_msg; + + //RN: fix for asynchronous notification of mouse leaving window not working + LLCoordWindow mouse_pos; + mWindow->getCursorPosition(&mouse_pos); + if (mouse_pos.mX < 0 || + mouse_pos.mY < 0 || + mouse_pos.mX > mWindowRect.getWidth() || + mouse_pos.mY > mWindowRect.getHeight()) + { + mMouseInWindow = FALSE; + } + else + { + mMouseInWindow = TRUE; + } + + S32 dx = mCurrentMousePoint.mX - mLastMousePoint.mX; + S32 dy = mCurrentMousePoint.mY - mLastMousePoint.mY; + mCurrentMouseDelta.set(dx,dy); + LLVector2 mouse_vel((F32)dx, (F32)dy); + mMouseVelocityStat.addValue(mouse_vel.magVec()); + + if (gNoRender) + { + return TRUE; + } + + S32 x = mCurrentMousePoint.mX; + S32 y = mCurrentMousePoint.mY; + MASK mask = gKeyboard->currentMask(TRUE); + + // clean up current focus + LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus(); + if (cur_focus) + { + if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) + { + gFocusMgr.releaseFocusIfNeeded(cur_focus); + + LLView* parent = cur_focus->getParent(); + LLView* focus_root = cur_focus->findRootMostFocusRoot(); + while(parent) + { + if (parent->isCtrl() && + (((LLUICtrl*)parent)->hasTabStop() || parent == focus_root) && + !((LLUICtrl*)parent)->getIsChrome() && + parent->isInVisibleChain() && + parent->isInEnabledChain()) + { + if (!parent->focusFirstItem()) + { + ((LLUICtrl*)parent)->setFocus(TRUE); + } + break; + } + parent = parent->getParent(); + } + } + else if (cur_focus->isFocusRoot()) + { + // focus roots keep trying to delegate focus to their first valid descendant + // this assumes that focus roots are not valid focus holders on their own + cur_focus->focusFirstItem(); + } + } + + // Show joints while in edit mode and hold down alt key. + if (gHUDManager) + { + if (gSavedSettings.getBOOL("AltShowsPhysical") + || (gFloaterTools && gFloaterTools->getVisible())) + { + gHUDManager->toggleShowPhysical( mask & MASK_ALT ); + } + } + + BOOL handled = FALSE; + + BOOL handled_by_top_view = FALSE; + LLView* top_view = gFocusMgr.getTopView(); + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + // Pass hover events to object capturing mouse events. + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + handled = mouse_captor->handleHover(local_x, local_y, mask); + if (LLView::sDebugMouseHandling) + { + llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl; + } + + if( !handled ) + { + lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl; + } + } + else + { + if (top_view) + { + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + handled = top_view->pointInView(local_x, local_y) && top_view->handleHover(local_x, local_y, mask); + handled_by_top_view = TRUE; + } + + if ( !handled ) + { + // x and y are from last time mouse was in window + // mMouseInWindow tracks *actual* mouse location + if (mMouseInWindow && mRootView->handleHover(x, y, mask) ) + { + if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg) + { + last_handle_msg = LLView::sMouseHandlerMessage; + llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl; + } + LLView::sMouseHandlerMessage = ""; + handled = TRUE; + } + else if (LLView::sDebugMouseHandling) + { + if (last_handle_msg != "") + { + last_handle_msg = ""; + llinfos << "Hover not handled by view" << llendl; + } + } + } + + if( !handled ) + { + lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl; + } + } + + //FIXME: sometimes tools handle the mouse as a captor, so this + // logic is a little confusing + LLTool *tool = NULL; + if (gToolMgr && gHoverView) + { + tool = gToolMgr->getCurrentTool(mask); + + if(!handled && tool) + { + handled = tool->handleHover(x, y, mask); + + if (!mWindow->isCursorHidden()) + { + gHoverView->updateHover(tool); + } + } + else + { + // Cancel hovering if any UI element handled the event. + gHoverView->cancelHover(); + } + + // Suppress the toolbox view if our source tool was the pie tool, + // and we've overridden to something else. + mSuppressToolbox = + (gToolMgr->getCurrentTool(MASK_NONE) == gToolPie) && + (gToolMgr->getCurrentTool(mask) != gToolPie); + + } + + //llinfos << (mToolTipBlocked ? "BLOCKED" : "NOT BLOCKED") << llendl; + // Show a new tool tip (or update one that is alrady shown) + BOOL tool_tip_handled = FALSE; + LLString tool_tip_msg; + F32 tooltip_delay = gSavedSettings.getF32( "ToolTipDelay" ); + //HACK: hack for tool-based tooltips which need to pop up more quickly + //Also for show xui names as tooltips debug mode + if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames) + { + tooltip_delay = gSavedSettings.getF32( "DragAndDropToolTipDelay" ); + } + if( handled && + !mToolTipBlocked && + (gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay) && + !mWindow->isCursorHidden() ) + { + LLRect screen_sticky_rect; + + if (mouse_captor) + { + S32 local_x, local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + tool_tip_handled = mouse_captor->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect ); + } + else if (handled_by_top_view) + { + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + tool_tip_handled = top_view->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect ); + } + else + { + tool_tip_handled = mRootView->handleToolTip(x, y, tool_tip_msg, &screen_sticky_rect ); + } + + if( tool_tip_handled && !tool_tip_msg.empty() ) + { + mToolTipStickyRect = screen_sticky_rect; + mToolTip->setWrappedText( tool_tip_msg, 200 ); + mToolTip->reshapeToFitText(); + mToolTip->setOrigin( x, y ); + LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0); + mToolTip->translateIntoRect( virtual_window_rect, FALSE ); + mToolTip->setVisible( TRUE ); + } + } + + if (tool != gToolNull && tool != gToolDragAndDrop && !gSavedSettings.getBOOL("FreezeTime")) + { + LLMouseHandler *captor = gFocusMgr.getMouseCapture(); + // With the null tool or drag and drop tool, don't muck + // with visibility. + + if (gFloaterTools->isMinimized() || + (tool != gToolPie // not default tool + && tool != gToolGun // not coming out of mouselook + && !mSuppressToolbox // not override in third person + && gCurrentToolset != gFaceEditToolset // not special mode + && gCurrentToolset != gMouselookToolset + && (!captor || captor->isView())) // not dragging + ) + { + // Force floater tools to be visible (unless minimized) + if (!gFloaterTools->isMinimized() && !gFloaterTools->getVisible()) + { + gFloaterTools->open(); + } + // Update the location of the blue box tool popup + LLCoordGL select_center_screen; + gFloaterTools->updatePopup( select_center_screen, mask ); + } + else + { + gFloaterTools->setVisible(FALSE); + } + } + if (gToolBar) + { + gToolBar->refresh(); + } + + if (gChatBar) + { + gChatBar->refresh(); + } + + if (gOverlayBar) + { + gOverlayBar->refresh(); + } + + // Update rectangles for the various toolbars + if (gToolBar && gChatBar && gOverlayBar && gNotifyBoxView && gConsole) + { + LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1); + if (gToolBar->getVisible()) + { + gToolBar->setRect(bar_rect); + bar_rect.translate(0, STATUS_BAR_HEIGHT-1); + } + + if (gChatBar->getVisible()) + { + // fix up the height + LLRect chat_bar_rect = bar_rect; + chat_bar_rect.mTop = chat_bar_rect.mBottom + CHAT_BAR_HEIGHT + 1; + gChatBar->setRect(chat_bar_rect); + bar_rect.translate(0, CHAT_BAR_HEIGHT-1); + } + + LLRect notify_box_rect = gNotifyBoxView->getRect(); + notify_box_rect.mBottom = bar_rect.mBottom; + gNotifyBoxView->reshape(notify_box_rect.getWidth(), notify_box_rect.getHeight()); + gNotifyBoxView->setRect(notify_box_rect); + + // make sure floaters snap to visible rect by adjusting floater view rect + LLRect floater_rect = gFloaterView->getRect(); + if (floater_rect.mBottom != bar_rect.mBottom+1) + { + floater_rect.mBottom = bar_rect.mBottom+1; + // Don't bounce the floaters up and down. + gFloaterView->reshape(floater_rect.getWidth(), floater_rect.getHeight(), + TRUE, ADJUST_VERTICAL_NO); + gFloaterView->setRect(floater_rect); + } + + if (gOverlayBar->getVisible()) + { + LLRect overlay_rect = bar_rect; + overlay_rect.mTop = overlay_rect.mBottom + OVERLAY_BAR_HEIGHT; + + // Fitt's Law: Push buttons flush with bottom of screen if + // nothing else visible. + if (!gToolBar->getVisible() + && !gChatBar->getVisible()) + { + // FIXME: this is highly depenent on the XML describing the position of the buttons + overlay_rect.translate(0, 0); + } + + gOverlayBar->setRect(overlay_rect); + gOverlayBar->updateRect(); + bar_rect.translate(0, gOverlayBar->getRect().getHeight()); + + gFloaterView->setSnapOffsetBottom(OVERLAY_BAR_HEIGHT); + } + else + { + gFloaterView->setSnapOffsetBottom(0); + } + + // fix rectangle of bottom panel focus indicator + if(gBottomPanel && gBottomPanel->getFocusIndicator()) + { + LLRect focus_rect = gBottomPanel->getFocusIndicator()->getRect(); + focus_rect.mTop = (gToolBar->getVisible() ? STATUS_BAR_HEIGHT : 0) + + (gChatBar->getVisible() ? CHAT_BAR_HEIGHT : 0) - 2; + gBottomPanel->getFocusIndicator()->setRect(focus_rect); + } + + // Always update console + LLRect console_rect = gConsole->getRect(); + console_rect.mBottom = bar_rect.mBottom + 8; + gConsole->reshape(console_rect.getWidth(), console_rect.getHeight()); + gConsole->setRect(console_rect); + } + + mLastMousePoint = mCurrentMousePoint; + + // last ditch force of edit menu to selection manager + if (gEditMenuHandler == NULL && gSelectMgr && gSelectMgr->getObjectCount()) + { + gEditMenuHandler = gSelectMgr; + } + + if (gFloaterView->getCycleMode()) + { + // sync all floaters with their focus state + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0) + { + // control key no longer held down, finish cycle mode + gFloaterView->setCycleMode(FALSE); + + gFloaterView->syncFloaterTabOrder(); + } + else + { + // user holding down CTRL, don't update tab order of floaters + } + } + else + { + // update focused floater + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + // make sure floater visible order is in sync with tab order + gFloaterView->syncFloaterTabOrder(); + } + + if (gSavedSettings.getBOOL("ChatBarStealsFocus") && gChatBar && gFocusMgr.getKeyboardFocus() == NULL && gChatBar->getVisible()) + { + gChatBar->startChat(NULL); + } + + // sync land selection with edit and about land dialogs + if (gParcelMgr && !LLFloaterLand::floaterVisible() && (!gFloaterTools || !gFloaterTools->getVisible())) + { + gParcelMgr->deselectLand(); + } + + return handled; +} + + +void LLViewerWindow::saveLastMouse(const LLCoordGL &point) +{ + // Store last mouse location. + // If mouse leaves window, pretend last point was on edge of window + if (point.mX < 0) + { + mCurrentMousePoint.mX = 0; + } + else if (point.mX > getWindowWidth()) + { + mCurrentMousePoint.mX = getWindowWidth(); + } + else + { + mCurrentMousePoint.mX = point.mX; + } + + if (point.mY < 0) + { + mCurrentMousePoint.mY = 0; + } + else if (point.mY > getWindowHeight() ) + { + mCurrentMousePoint.mY = getWindowHeight(); + } + else + { + mCurrentMousePoint.mY = point.mY; + } +} + + +// Draws the selection outlines for the currently selected objects +// Must be called after displayObjects is called, which sets the mGLName parameter +// NOTE: This function gets called 3 times: +// render_ui_3d: FALSE, FALSE, TRUE +// renderObjectsForSelect: TRUE, pick_parcel_wall, FALSE +// render_hud_elements: FALSE, FALSE, FALSE +void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud ) +{ + LLViewerObject* object; + + if (!for_hud && !for_gl_pick) + { + // Call this once and only once + gSelectMgr->updateSilhouettes(); + } + + // Draw fence around land selections + if (for_gl_pick) + { + if (pick_parcel_walls) + { + gParcelMgr->renderParcelCollision(); + } + } + else if (( for_hud && gSelectMgr->getSelectType() == SELECT_TYPE_HUD) || + (!for_hud && gSelectMgr->getSelectType() != SELECT_TYPE_HUD)) + { + gSelectMgr->renderSilhouettes(for_hud); + + stop_glerror(); + + // setup HUD render + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount()) + { + LLBBox hud_bbox = gAgent.getAvatarObject()->getHUDBBox(); + + // set up transform to encompass bounding box of HUD + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f); + glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, depth); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame + glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f); + } + + // Render light for editing + if (LLSelectMgr::sRenderLightRadius) + { + LLGLEnable gls_blend(GL_BLEND); + LLGLEnable gls_cull(GL_CULL_FACE); + LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD) + { + F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom; + glScalef(zoom, zoom, zoom); + } + for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() ) + { + LLDrawable* drawable = object->mDrawable; + if (drawable && drawable->isLight()) + { + LLVOVolume* vovolume = drawable->getVOVolume(); + glPushMatrix(); + + LLVector3 center = drawable->getPositionAgent(); + glTranslatef(center[0], center[1], center[2]); + F32 scale = vovolume->getLightRadius(); + glScalef(scale, scale, scale); + + LLColor4 color(vovolume->getLightColor(), .5f); + glColor4fv(color.mV); + + F32 pixel_area = 100000.f; + // Render Outside + gSphere.render(pixel_area); + + // Render Inside + glCullFace(GL_FRONT); + gSphere.render(pixel_area); + glCullFace(GL_BACK); + + glPopMatrix(); + } + } + glPopMatrix(); + } + + // NOTE: The average position for the axis arrows of the selected objects should + // not be recalculated at this time. If they are, then group rotations will break. + + // Draw arrows at average center of all selected objects + LLTool* tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ); + if (tool) + { + if(tool->isAlwaysRendered()) + { + tool->render(); + } + else + { + if( !gSelectMgr->isEmpty() ) + { + BOOL moveable_object_selected = FALSE; + BOOL all_selected_objects_move = TRUE; + BOOL all_selected_objects_modify = TRUE; + BOOL selecting_linked_set = gSavedSettings.getBOOL("SelectLinkedSet"); + for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() ) + { + BOOL this_object_movable = FALSE; + if (object->permMove() && (object->permModify() || selecting_linked_set)) + { + moveable_object_selected = TRUE; + this_object_movable = TRUE; + } + all_selected_objects_move = all_selected_objects_move && this_object_movable; + all_selected_objects_modify = all_selected_objects_modify && object->permModify(); + } + + BOOL draw_handles = TRUE; + + if (tool == gToolTranslate && (!moveable_object_selected || !all_selected_objects_move)) + { + draw_handles = FALSE; + } + + if (tool == gToolRotate && (!moveable_object_selected || !all_selected_objects_move)) + { + draw_handles = FALSE; + } + + if ( !all_selected_objects_modify && tool == gToolStretch ) + { + draw_handles = FALSE; + } + + if( draw_handles ) + { + tool->render(); + } + } + } + if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount()) + { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + stop_glerror(); + } + } + } +} + +// Return a point near the clicked object representative of the place the object was clicked. +LLVector3d LLViewerWindow::clickPointInWorldGlobal(S32 x, S32 y_from_bot, LLViewerObject* clicked_object) const +{ + // create a normalized vector pointing from the camera center into the + // world at the location of the mouse click + LLVector3 mouse_direction_global = mouseDirectionGlobal( x, y_from_bot ); + + LLVector3d relative_object = clicked_object->getPositionGlobal() - gAgent.getCameraPositionGlobal(); + + // make mouse vector as long as object vector, so it touchs a point near + // where the user clicked on the object + mouse_direction_global *= (F32) relative_object.magVec(); + + LLVector3d new_pos; + new_pos.setVec(mouse_direction_global); + // transform mouse vector back to world coords + new_pos += gAgent.getCameraPositionGlobal(); + + return new_pos; +} + + +BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const +{ + BOOL intersect = FALSE; + +// U8 shape = objectp->mPrimitiveCode & LL_PCODE_BASE_MASK; + if (!intersect) + { + point_global = clickPointInWorldGlobal(x, y, objectp); + llinfos << "approx intersection at " << (objectp->getPositionGlobal() - point_global) << llendl; + } + else + { + llinfos << "good intersection at " << (objectp->getPositionGlobal() - point_global) << llendl; + } + + return intersect; +} + +void LLViewerWindow::hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent, BOOL pick_parcel_walls) +{ + if (gNoRender) + { + return; + } + + S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]); + S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]); + + F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32(); + + BOOL in_build_mode = gFloaterTools && gFloaterTools->getVisible(); + if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha) + { + // build mode allows interaction with all transparent objects + // "Show Debug Alpha" means no object actually transparent + pick_transparent = TRUE; + } + gPickTransparent = pick_transparent; + + if (gPickTransparent) + { + gPickAlphaTargetThreshold = 0.f; + gPickAlphaThreshold = llmax(gPickAlphaTargetThreshold, gPickAlphaThreshold - (delta_time * 0.7f)); + } + else + { + gPickAlphaTargetThreshold = 1.f; + gPickAlphaThreshold = llmin(gPickAlphaTargetThreshold, gPickAlphaThreshold + (delta_time * 0.7f)); + } + + gUseGLPick = FALSE; + mPickCallback = callback; + + // Default to not hitting anything + gLastHitPosGlobal.zeroVec(); + gLastHitObjectOffset.zeroVec(); + gLastHitObjectID.setNull(); + gLastHitObjectFace = -1; + + gLastHitNonFloraPosGlobal.zeroVec(); + gLastHitNonFloraObjectOffset.zeroVec(); + gLastHitNonFloraObjectID.setNull(); + gLastHitNonFloraObjectFace = -1; + + gLastHitParcelWall = FALSE; + + LLCamera pick_camera; + pick_camera.setOrigin(gCamera->getOrigin()); + pick_camera.setOriginAndLookAt(gCamera->getOrigin(), + gCamera->getUpAxis(), + gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot)); + pick_camera.setView(0.5f*DEG_TO_RAD); + pick_camera.setNear(gCamera->getNear()); + pick_camera.setFar(gCamera->getFar()); + pick_camera.setAspect(1.f); + + // save our drawing state + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + // build perspective transform and picking viewport + // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point. + // Don't limit the select distance for this pick. + // make viewport big enough to handle antialiased frame buffers + gCamera->setPerspective(FOR_SELECTION, scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4, FALSE); + pick_camera.calcAgentFrustumPlanes(gCamera->mAgentFrustum); + // make viewport big enough to handle antialiased frame buffers + glViewport(scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4); + stop_glerror(); + + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // Draw the objects so the user can select them. + // The starting ID is 1, since land is zero. + gObjectList.renderObjectsForSelect(pick_camera, pick_parcel_walls); + + stop_glerror(); + + // restore drawing state + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + setupViewport(); + + mPickPoint.set(x, y_from_bot); + mPickOffset.set(0, 0); + mPickMask = mask; + mPickPending = TRUE; + + // delay further event processing until we receive results of pick + mWindow->delayInputProcessing(); +} + +void LLViewerWindow::hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask)) +{ + // Performs the GL UI pick. + // Stores its results in global, gLastHitUIElement + if (gNoRender) + { + return; + } + + hitUIElementAsync(x, y, gKeyboard->currentMask(TRUE), NULL); + performPick(); + if (callback) + { + callback(x, y, gKeyboard->currentMask(TRUE)); + } +} + +//RN: this currently doesn't do anything +void LLViewerWindow::hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask)) +{ + if (gNoRender) + { + return; + } + +// F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32(); + + gUseGLPick = FALSE; + mPickCallback = callback; + + // Default to not hitting anything + gLastHitUIElement = 0; + + LLCamera pick_camera; + pick_camera.setOrigin(gCamera->getOrigin()); + pick_camera.setOriginAndLookAt(gCamera->getOrigin(), + gCamera->getUpAxis(), + gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot)); + pick_camera.setView(0.5f*DEG_TO_RAD); + pick_camera.setNear(gCamera->getNear()); + pick_camera.setFar(gCamera->getFar()); + pick_camera.setAspect(1.f); + + // save our drawing state + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + // build orthogonal transform and picking viewport + // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point. + // Don't limit the select distance for this pick. + gViewerWindow->setup2DRender(); + const LLVector2& display_scale = gViewerWindow->getDisplayScale(); + glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + // make viewport big enough to handle antialiased frame buffers + glViewport(x - (PICK_HALF_WIDTH + 2), y_from_bot - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4); + stop_glerror(); + + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + // Draw the objects so the user can select them. + // The starting ID is 1, since land is zero. + //gViewerWindow->drawForSelect(); + + stop_glerror(); + + // restore drawing state + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + setupViewport(); + + mPickPoint.set(x, y_from_bot); + mPickOffset.set(0, 0); + mPickMask = mask; + mPickPending = TRUE; +} + +void LLViewerWindow::performPick() +{ + if (gNoRender || !mPickPending) + { + return; + } + + finishFastFrame(); + + mPickPending = FALSE; + U32 te_offset = NO_FACE; + + // find pick region that is fully onscreen + LLCoordGL scaled_pick_point = mPickPoint; + scaled_pick_point.mX = llclamp(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayWidth() - PICK_HALF_WIDTH); + scaled_pick_point.mY = llclamp(llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayHeight() - PICK_HALF_WIDTH); + + glReadPixels(scaled_pick_point.mX - PICK_HALF_WIDTH, scaled_pick_point.mY - PICK_HALF_WIDTH, PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer); + + S32 pixel_index = PICK_HALF_WIDTH * PICK_DIAMETER + PICK_HALF_WIDTH; + S32 name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2]; + gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3]; + + if (name >= (S32)GL_NAME_UI_RESERVED && name < (S32)GL_NAME_INDEX_OFFSET) + { + // hit a UI element + gLastHitUIElement = name; + if (mPickCallback) + { + mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask); + } + } + + //imdebug("rgba rbga=bbba b=8 w=%d h=%d %p", PICK_DIAMETER, PICK_DIAMETER, mPickBuffer); + + S32 x_offset = mPickPoint.mX - llround((F32)scaled_pick_point.mX / mDisplayScale.mV[VX]); + S32 y_offset = mPickPoint.mY - llround((F32)scaled_pick_point.mY / mDisplayScale.mV[VY]); + + + // we hit nothing, scan surrounding pixels for something useful + if (!name) + { + S32 closest_distance = 10000; + //S32 closest_pick_name = 0; + for (S32 col = 0; col < PICK_DIAMETER; col++) + { + for (S32 row = 0; row < PICK_DIAMETER; row++) + { + S32 distance_squared = (llabs(col - x_offset - PICK_HALF_WIDTH) * llabs(col - x_offset - PICK_HALF_WIDTH)) + (llabs(row - y_offset - PICK_HALF_WIDTH) * llabs(row - y_offset - PICK_HALF_WIDTH)); + pixel_index = row * PICK_DIAMETER + col; + S32 test_name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2]; + gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3]; + if (test_name && distance_squared < closest_distance) + { + closest_distance = distance_squared; + name = test_name; + gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3]; + mPickOffset.mX = col - PICK_HALF_WIDTH; + mPickOffset.mY = row - PICK_HALF_WIDTH; + } + } + } + } + + if (name) + { + mPickPoint.mX += llround((F32)mPickOffset.mX * mDisplayScale.mV[VX]); + mPickPoint.mY += llround((F32)mPickOffset.mY * mDisplayScale.mV[VY]); + } + + if (gPickFaces) + { + te_offset = ((U32)name >> 20); + name &= 0x000fffff; + // don't clear gPickFaces, as we still need to check for UV coordinates + } + + LLViewerObject *objectp = NULL; + + // Frontmost non-foreground object that isn't trees or grass + LLViewerObject* nonflora_objectp = NULL; + S32 nonflora_name = -1; + S32 nonflora_te_offset = NO_FACE; + + if (name == (S32)GL_NAME_PARCEL_WALL) + { + gLastHitParcelWall = TRUE; + } + + gLastHitHUDIcon = NULL; + + objectp = gObjectList.getSelectedObject(name); + if (objectp) + { + if (objectp->mbCanSelect) + { + te_offset = (te_offset == 16) ? NO_FACE : te_offset; + + // If the hit object isn't a plant, store it as the frontmost non-flora object. + LLPCode pcode = objectp->getPCode(); + if( (LL_PCODE_LEGACY_GRASS != pcode) && + (LL_PCODE_LEGACY_TREE != pcode) && + (LL_PCODE_TREE_NEW != pcode)) + { + nonflora_objectp = objectp; + nonflora_name = name; + nonflora_te_offset = te_offset; + } + } + else + { + //llinfos << "Hit object you can't select" << llendl; + } + } + else + { + // was this name referring to a hud icon? + gLastHitHUDIcon = LLHUDIcon::handlePick(name); + } + + analyzeHit( + mPickPoint.mX, mPickPoint.mY, objectp, te_offset, + &gLastHitObjectID, &gLastHitObjectFace, &gLastHitPosGlobal, &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord ); + + if (objectp && !gLastHitObjectID.isNull()) + { + gLastHitObjectOffset = gAgent.calcFocusOffset(objectp, mPickPoint.mX, mPickPoint.mY); + } + + if( objectp == nonflora_objectp ) + { + gLastHitNonFloraObjectID = gLastHitObjectID; + gLastHitNonFloraObjectFace = gLastHitObjectFace; + gLastHitNonFloraPosGlobal = gLastHitPosGlobal; + gLastHitNonFloraObjectOffset= gLastHitObjectOffset; + } + else + { + analyzeHit( mPickPoint.mX, mPickPoint.mY, nonflora_objectp, nonflora_te_offset, + &gLastHitNonFloraObjectID, &gLastHitNonFloraObjectFace, &gLastHitNonFloraPosGlobal, + &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord); + + if( nonflora_objectp ) + { + gLastHitNonFloraObjectOffset = gAgent.calcFocusOffset(nonflora_objectp, mPickPoint.mX, mPickPoint.mY); + } + } + + if (mPickCallback) + { + mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask); + } + + gPickFaces = FALSE; +} + +// Performs the GL object/land pick. +// Stores its results in globals, gHit* +void LLViewerWindow::hitObjectOrLandGlobalImmediate(S32 x, S32 y_from_bot, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent) +{ + if (gNoRender) + { + return; + } + + hitObjectOrLandGlobalAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent); + performPick(); + if (callback) + { + callback(x, y_from_bot, gKeyboard->currentMask(TRUE)); + } +} + +LLViewerObject* LLViewerWindow::getObjectUnderCursor(const F32 depth) +{ + S32 x = getCurrentMouseX(); + S32 y = getCurrentMouseY(); + + LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); + LLVector3 camera_pos_global = gCamera->getOrigin(); + LLVector3 pick_end = camera_pos_global + mouse_direction_global * depth; + LLVector3 collision_point; + return gPipeline.pickObject(camera_pos_global, pick_end, collision_point); +} + +void LLViewerWindow::analyzeHit( + S32 x, // input + S32 y_from_bot, // input + LLViewerObject* objectp, // input + U32 te_offset, // input + LLUUID* hit_object_id_p,// output + S32* hit_face_p, // output + LLVector3d* hit_pos_p, // output + BOOL* hit_land, // output + F32* hit_u_coord, // output + F32* hit_v_coord) // output +{ + // Clean up inputs + S32 face = -1; + + if (te_offset != NO_FACE ) + { + face = te_offset; + } + + *hit_land = FALSE; + + if (objectp) + { + if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH ) + { + // Hit land + *hit_land = TRUE; + + // put global position into land_pos + LLVector3d land_pos; + if (mousePointOnLandGlobal(x, y_from_bot, &land_pos)) + { + *hit_object_id_p = LLUUID::null; + *hit_face_p = -1; + + // Fudge the land focus a little bit above ground. + *hit_pos_p = land_pos + LLVector3d(0.f, 0.f, 0.1f); + //llinfos << "DEBUG Hit Land " << *hit_pos_p << llendl; + return; + } + else + { + //llinfos << "Hit land but couldn't find position" << llendl; + // Fall through to "Didn't hit anything" + } + } + else + { + *hit_object_id_p = objectp->mID; + *hit_face_p = face; + + // Hit an object + if (objectp->isAvatar()) + { + *hit_pos_p = gAgent.getPosGlobalFromAgent(((LLVOAvatar*)objectp)->mPelvisp->getWorldPosition()); + } + else if (objectp->mDrawable.notNull()) + { + *hit_pos_p = gAgent.getPosGlobalFromAgent(objectp->getRenderPosition()); + } + else + { + // regular object + *hit_pos_p = objectp->getPositionGlobal(); + } + + if (gPickFaces && face > -1 && + objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME && + face < objectp->mDrawable->getNumFaces()) + { + // render red-blue gradient to get 1/256 precision + // then render green grid to get final 1/4096 precision + S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]); + S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]); + const S32 UV_PICK_WIDTH = 41; + const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2; + U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4]; + S32 pick_face = ((LLVOVolume*)objectp)->getAllTEsSame() ? 0 : face; + LLFace* facep = objectp->mDrawable->getFace(objectp->getFaceIndexOffset() + pick_face); + gCamera->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE); + glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH); + gPipeline.renderFaceForUVSelect(facep); + + glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer); + U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)]; + *hit_u_coord = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f; + *hit_v_coord = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f; + } + else + { + *hit_u_coord = 0.f; + *hit_v_coord = 0.f; + } + + //llinfos << "DEBUG Hit Object " << *hit_pos_p << llendl; + return; + } + } + + // Didn't hit anything. + *hit_object_id_p = LLUUID::null; + *hit_face_p = -1; + *hit_pos_p = LLVector3d::zero; + *hit_u_coord = 0.f; + *hit_v_coord = 0.f; + //llinfos << "DEBUG Hit Nothing " << llendl; +} + + +void LLViewerWindow::requestFastFrame(LLView *view) +{ + if (!mPickPending && + mWindow->getSwapMethod() != LLWindow::SWAP_METHOD_UNDEFINED && + gStartupState >= STATE_STARTED && + gSavedSettings.getBOOL("RenderFastUI") && + !gbCapturing) + { + if (!mFastFrameTimer.getStarted()) + { + // we're double buffered, so when first requesting a fast ui update + // we need to render the scene again so that the front and back buffers + // are synced + mRenderFullFrame = TRUE; + } + // calculation new expiration time and reset timer + F32 expiration; + if (mFastFrameTimer.hasExpired()) + { + expiration = FAST_FRAME_INCREMENT; + } + else + { + expiration = llmin(MAX_FAST_FRAME_TIME, mFastFrameTimer.getTimeToExpireF32() + FAST_FRAME_INCREMENT); + } + + mFastFrameTimer.start(); + mFastFrameTimer.setTimerExpirySec(expiration); + + LLView::sFastFrameView = view->getRootMostFastFrameView(); + if (!LLView::sFastFrameView) + { + LLView::sFastFrameView = view; + } + } +} + + +// Returns unit vector relative to camera +// indicating direction of point on screen x,y +LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const +{ + // find vertical field of view + F32 fov = gCamera->getView(); + + // find screen resolution + S32 height = getWindowHeight(); + S32 width = getWindowWidth(); + + // calculate pixel distance to screen + F32 distance = (height / 2.f) / (tan(fov / 2.f)); + + // calculate click point relative to middle of screen + F32 click_x = x - width / 2.f; + F32 click_y = y - height / 2.f; + + // compute mouse vector + LLVector3 mouse_vector = distance * gCamera->getAtAxis() + - click_x * gCamera->getLeftAxis() + + click_y * gCamera->getUpAxis(); + + mouse_vector.normVec(); + + return mouse_vector; +} + + +// Returns unit vector relative to camera in camera space +// indicating direction of point on screen x,y +LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const +{ + // find vertical field of view + F32 fov_height = gCamera->getView(); + F32 fov_width = fov_height * gCamera->getAspect(); + + // find screen resolution + S32 height = getWindowHeight(); + S32 width = getWindowWidth(); + + // calculate click point relative to middle of screen + F32 click_x = (((F32)x / (F32)width) - 0.5f) * fov_width * -1.f; + F32 click_y = (((F32)y / (F32)height) - 0.5f) * fov_height; + + // compute mouse vector + LLVector3 mouse_vector = LLVector3(0.f, 0.f, -1.f); + LLQuaternion mouse_rotate; + mouse_rotate.setQuat(click_y, click_x, 0.f); + + mouse_vector = mouse_vector * mouse_rotate; + // project to z = -1 plane; + mouse_vector = mouse_vector * (-1.f / mouse_vector.mV[VZ]); + + return mouse_vector; +} + + + +BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y, + const LLVector3d &plane_point_global, + const LLVector3 &plane_normal_global) +{ + LLVector3d mouse_direction_global_d; + + mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y)); + LLVector3d plane_normal_global_d; + plane_normal_global_d.setVec(plane_normal_global); + F64 mouse_look_at_scale = (plane_normal_global_d * (plane_point_global - gAgent.getCameraPositionGlobal())) + / (plane_normal_global_d * mouse_direction_global_d); + point = gAgent.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d; + + return mouse_look_at_scale > 0.0; +} + + +// Returns global position +BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_position_global) +{ + LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); + F32 mouse_dir_scale; + BOOL hit_land = FALSE; + LLViewerRegion *regionp; + F32 land_z; + const F32 FIRST_PASS_STEP = 1.0f; // meters + const F32 SECOND_PASS_STEP = 0.1f; // meters + LLVector3d camera_pos_global; + + camera_pos_global = gAgent.getCameraPositionGlobal(); + LLVector3d probe_point_global; + LLVector3 probe_point_region; + + // walk forwards to find the point + for (mouse_dir_scale = FIRST_PASS_STEP; mouse_dir_scale < gAgent.mDrawDistance; mouse_dir_scale += FIRST_PASS_STEP) + { + LLVector3d mouse_direction_global_d; + mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale); + probe_point_global = camera_pos_global + mouse_direction_global_d; + + regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global); + + if (!regionp) + { + // ...we're outside the world somehow + continue; + } + + S32 i = (S32) (probe_point_region.mV[VX]/regionp->getLand().getMetersPerGrid()); + S32 j = (S32) (probe_point_region.mV[VY]/regionp->getLand().getMetersPerGrid()); + S32 grids_per_edge = (S32) regionp->getLand().mGridsPerEdge; + if ((i >= grids_per_edge) || (j >= grids_per_edge)) + { + //llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl; + continue; + } + + land_z = regionp->getLand().resolveHeightRegion(probe_point_region); + + //llinfos << "mousePointOnLand initial z " << land_z << llendl; + + if (probe_point_region.mV[VZ] < land_z) + { + // ...just went under land + + // cout << "under land at " << probe_point << " scale " << mouse_vec_scale << endl; + + hit_land = TRUE; + break; + } + } + + + if (hit_land) + { + // Don't go more than one step beyond where we stopped above. + // This can't just be "mouse_vec_scale" because floating point error + // will stop the loop before the last increment.... X - 1.0 + 0.1 + 0.1 + ... + 0.1 != X + F32 stop_mouse_dir_scale = mouse_dir_scale + FIRST_PASS_STEP; + + // take a step backwards, then walk forwards again to refine position + for ( mouse_dir_scale -= FIRST_PASS_STEP; mouse_dir_scale <= stop_mouse_dir_scale; mouse_dir_scale += SECOND_PASS_STEP) + { + LLVector3d mouse_direction_global_d; + mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale); + probe_point_global = camera_pos_global + mouse_direction_global_d; + + regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global); + + if (!regionp) + { + // ...we're outside the world somehow + continue; + } + + /* + i = (S32) (local_probe_point.mV[VX]/regionp->getLand().getMetersPerGrid()); + j = (S32) (local_probe_point.mV[VY]/regionp->getLand().getMetersPerGrid()); + if ((i >= regionp->getLand().mGridsPerEdge) || (j >= regionp->getLand().mGridsPerEdge)) + { + // llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl; + continue; + } + land_z = regionp->getLand().mSurfaceZ[ i + j * (regionp->getLand().mGridsPerEdge) ]; + */ + + land_z = regionp->getLand().resolveHeightRegion(probe_point_region); + + //llinfos << "mousePointOnLand refine z " << land_z << llendl; + + if (probe_point_region.mV[VZ] < land_z) + { + // ...just went under land again + + *land_position_global = probe_point_global; + return TRUE; + } + } + } + + return FALSE; +} + +// Saves an image to the harddrive as "SnapshotX" where X >= 1. +BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw) +{ + if (! raw) + { + return FALSE; + } + + // Get a directory if this is the first time. + if (strlen(sSnapshotDir) == 0) + { + LLString proposed_name( sSnapshotBaseName ); + proposed_name.append( ".bmp" ); + + // pick a directory in which to save + LLFilePicker& picker = LLFilePicker::instance(); + if (!picker.getSaveFile(LLFilePicker::FFSAVE_BMP, proposed_name.c_str())) + { + // Clicked cancel + return FALSE; + } + + // Copy the directory + file name + char directory[LL_MAX_PATH]; + strcpy(directory, picker.getFirstFile()); + + // Smash the file extension + S32 length = strlen(directory); + S32 index = length; + + // Back up over ".bmp" + index -= 4; + if (index >= 0 && directory[index] == '.') + { + directory[index] = '\0'; + } + else + { + index = length; + } + + // Find trailing backslash + while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0]) + { + index--; + } + + // If we found one, truncate the string there + if (index >= 0) + { + if (index + 1 <= length) + { + strcpy(LLViewerWindow::sSnapshotBaseName, directory + index + 1); + } + + index++; + directory[index] = '\0'; + strcpy(LLViewerWindow::sSnapshotDir, directory); + } + } + + // Look for an unused file name + LLString filepath; + S32 i = 1; + S32 err = 0; + + do + { + char extension[100]; + sprintf( extension, "_%.3d.bmp", i ); + filepath = sSnapshotDir; + filepath += sSnapshotBaseName; + filepath += extension; + + struct stat stat_info; + err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info ); + i++; + } + while( -1 != err ); // search until the file is not found (i.e., stat() gives an error). + + LLPointer<LLImageBMP> bmp_image = new LLImageBMP; + LLImageBase::setSizeOverride(TRUE); + BOOL success = bmp_image->encode(raw); + if( success ) + { + success = bmp_image->save(filepath); + } + else + { + llwarns << "Unable to encode bmp snapshot" << llendl; + } + LLImageBase::setSizeOverride(FALSE); + + return success; +} + +void LLViewerWindow::saveMovieNumbered(void*) +{ + if (!gbCapturing) + { + // Get a directory if this is the first time. + if (strlen(sSnapshotDir) == 0) + { + LLString proposed_name( sMovieBaseName ); +#if LL_DARWIN + proposed_name.append( ".mov" ); +#else + proposed_name.append( ".avi" ); +#endif + + // pick a directory in which to save + LLFilePicker &picker = LLFilePicker::instance(); + if (!picker.getSaveFile(LLFilePicker::FFSAVE_AVI, proposed_name.c_str())) + { + // Clicked cancel + return; + } + + // Copy the directory + file name + char directory[LL_MAX_PATH]; + strcpy(directory, picker.getFirstFile()); + + // Smash the file extension + S32 length = strlen(directory); + S32 index = length; + + // Back up over ".bmp" + index -= 4; + if (index >= 0 && directory[index] == '.') + { + directory[index] = '\0'; + } + else + { + index = length; + } + + // Find trailing backslash + while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0]) + { + index--; + } + + // If we found one, truncate the string there + if (index >= 0) + { + if (index + 1 <= length) + { + strcpy(LLViewerWindow::sMovieBaseName, directory + index + 1); + } + + index++; + directory[index] = '\0'; + strcpy(LLViewerWindow::sSnapshotDir, directory); + } + } + + // Look for an unused file name + LLString filepath; + S32 i = 1; + S32 err = 0; + + do + { + char extension[100]; +#if LL_DARWIN + sprintf( extension, "_%.3d.mov", i ); +#else + sprintf( extension, "_%.3d.avi", i ); +#endif + filepath.assign( sSnapshotDir ); + filepath.append( sMovieBaseName ); + filepath.append( extension ); + + struct stat stat_info; + err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info ); + i++; + } + while( -1 != err ); // search until the file is not found (i.e., stat() gives an error). + S32 x = gViewerWindow->getWindowWidth(); + S32 y = gViewerWindow->getWindowHeight(); + + gbCapturing = TRUE; + gMovieMaker.StartCapture((char *)filepath.c_str(), x, y); + } + else + { + gMovieMaker.EndCapture(); + gbCapturing = FALSE; + } +} + +static S32 BORDERHEIGHT = 0; +static S32 BORDERWIDTH = 0; + +void LLViewerWindow::movieSize(S32 new_width, S32 new_height) +{ + LLCoordScreen size; + gViewerWindow->mWindow->getSize(&size); + if ( (size.mX != new_width + BORDERWIDTH) + ||(size.mY != new_height + BORDERHEIGHT)) + { + S32 x = gViewerWindow->getWindowWidth(); + S32 y = gViewerWindow->getWindowHeight(); + BORDERWIDTH = size.mX - x; + BORDERHEIGHT = size.mY- y; + LLCoordScreen new_size(new_width + BORDERWIDTH, + new_height + BORDERHEIGHT); + BOOL disable_sync = gSavedSettings.getBOOL("DisableVerticalSync"); + if (gViewerWindow->mWindow->getFullscreen()) + { + gViewerWindow->changeDisplaySettings(FALSE, + new_size, + disable_sync, + TRUE); + } + else + { + gViewerWindow->mWindow->setSize(new_size); + } + } +} + +BOOL LLViewerWindow::saveSnapshot( const LLString& filepath, S32 image_width, S32 image_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) +{ + llinfos << "Saving snapshot to: " << filepath << llendl; + + LLPointer<LLImageRaw> raw = new LLImageRaw; + BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, show_ui, do_rebuild); + + if (success) + { + LLPointer<LLImageBMP> bmp_image = new LLImageBMP; + success = bmp_image->encode(raw); + if( success ) + { + success = bmp_image->save(filepath); + } + else + { + llwarns << "Unable to encode bmp snapshot" << llendl; + } + } + else + { + llwarns << "Unable to capture raw snapshot" << llendl; + } + + return success; +} + + +void LLViewerWindow::playSnapshotAnimAndSound() +{ + gAgent.sendAnimationRequest(ANIM_AGENT_SNAPSHOT, ANIM_REQUEST_START); + send_sound_trigger(LLUUID(gSavedSettings.getString("UISndSnapshot")), 1.0f); +} + + +// Saves the image from the screen to the specified filename and path. +BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, + BOOL keep_window_aspect, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) +{ + F32 image_aspect_ratio = ((F32)image_width) / ((F32)image_height); + F32 window_aspect_ratio = ((F32)getWindowWidth()) / ((F32)getWindowHeight()); + + if ((!gWorldPointer) || + (!raw)) + { + return FALSE; + } + + // IW 3/5/04 We don'a wan' nunna yer fest frumes har! + finishFastFrame(); + + // PRE SNAPSHOT + + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + setCursor(UI_CURSOR_WAIT); + + // Hide all the UI widgets first and draw a frame + BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI); + + if ( prev_draw_ui != show_ui) + { + LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI); + } + + BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD); + if (hide_hud) + { + LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD); + } + + // Copy screen to a buffer + // crop sides or top and bottom, if taking a snapshot of different aspect ratio + // from window + S32 snapshot_width = mWindowRect.getWidth(); + S32 snapshot_height = mWindowRect.getHeight(); + if (!keep_window_aspect) + { + if (image_aspect_ratio > window_aspect_ratio) + { + snapshot_height = llround((F32)snapshot_width / image_aspect_ratio); + } + else if (image_aspect_ratio < window_aspect_ratio) + { + snapshot_width = llround((F32)snapshot_height * image_aspect_ratio); + } + } + + F32 scale_factor = llmax(1.f, (F32)image_width / snapshot_width, (F32)image_height / snapshot_height); + raw->resize(llfloor(snapshot_width*scale_factor), llfloor(snapshot_height *scale_factor), type == SNAPSHOT_TYPE_DEPTH ? 4 : 3); + + BOOL high_res = scale_factor > 1.f; + if (high_res) + { + send_agent_pause(); + //rescale fonts + initFonts(scale_factor); + LLHUDText::reshape(); + } + + // SNAPSHOT + S32 window_width = mWindowRect.getWidth(); + S32 window_height = mWindowRect.getHeight(); + S32 buffer_x_offset = llfloor(((window_width - snapshot_width) * scale_factor) / 2.f); + S32 buffer_y_offset = llfloor(((window_height - snapshot_height) * scale_factor) / 2.f); + + S32 output_buffer_offset_y = 0; + + F32 depth_conversion_factor_1 = (gCamera->getFar() + gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear()); + F32 depth_conversion_factor_2 = (gCamera->getFar() - gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear()); + + for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y) + { + S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);; + // handle fractional columns + U32 read_height = llmax(0, (window_height - subimage_y_offset) - + llmax(0, (window_height * (subimage_y + 1)) - (buffer_y_offset + raw->getHeight()))); + + S32 output_buffer_offset_x = 0; + for (int subimage_x = 0; subimage_x < scale_factor; ++subimage_x) + { + gDisplaySwapBuffers = FALSE; + if (type == SNAPSHOT_TYPE_OBJECT_ID) + { + glClearColor(0.f, 0.f, 0.f, 1.f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + gCamera->setZoomParameters(scale_factor, subimage_x+(subimage_y*llceil(scale_factor))); + setup3DRender(); + setupViewport(); + BOOL first_time_through = (subimage_x + subimage_y == 0); + gPickTransparent = FALSE; + gPickAlphaThreshold = 0.1f; + gObjectList.renderObjectsForSelect(*gCamera, FALSE, !first_time_through); + } + else + { + display(do_rebuild, scale_factor, subimage_x+(subimage_y*llceil(scale_factor))); + } + glFlush(); + S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width); + // handle fractional rows + U32 read_width = llmax(0, (window_width - subimage_x_offset) - + llmax(0, (window_width * (subimage_x + 1)) - (buffer_x_offset + raw->getWidth()))); + for(U32 out_y = 0; out_y < read_height ; out_y++) + { + if (type == SNAPSHOT_TYPE_OBJECT_ID || type == SNAPSHOT_TYPE_COLOR) + { + glReadPixels( + subimage_x_offset, out_y + subimage_y_offset, + read_width, 1, + GL_RGB, GL_UNSIGNED_BYTE, + raw->getData() + // current output pixel is beginning of buffer... + ( + (out_y * (raw->getWidth())) // ...plus iterated y... + + (window_width * subimage_x) // ...plus subimage start in x... + + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y... + - output_buffer_offset_x // ...minus buffer padding x... + - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y... + ) * 3 // times 3 bytes per pixel + ); + } + else // SNAPSHOT_TYPE_DEPTH + { + S32 output_buffer_offset = ( + (out_y * (raw->getWidth())) // ...plus iterated y... + + (window_width * subimage_x) // ...plus subimage start in x... + + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y... + - output_buffer_offset_x // ...minus buffer padding x... + - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y... + ) * 4; // times 4 bytes per pixel + + glReadPixels( + subimage_x_offset, out_y + subimage_y_offset, + read_width, 1, + GL_DEPTH_COMPONENT, GL_FLOAT, + raw->getData() + output_buffer_offset// current output pixel is beginning of buffer... + ); + + for (S32 i = output_buffer_offset; i < output_buffer_offset + (S32)read_width * 4; i += 4) + { + F32 depth_float = *(F32*)(raw->getData() + i); + + F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float * depth_conversion_factor_2)); + U8 depth_byte = F32_to_U8(linear_depth_float, gCamera->getNear(), gCamera->getFar()); + *(raw->getData() + i + 0) = depth_byte; + *(raw->getData() + i + 1) = depth_byte; + *(raw->getData() + i + 2) = depth_byte; + *(raw->getData() + i + 3) = 255; + } + } + } + output_buffer_offset_x += subimage_x_offset; + stop_glerror(); + } + output_buffer_offset_y += subimage_y_offset; + } + + // POST SNAPSHOT + if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI); + } + + if (hide_hud) + { + LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD); + } + + if (high_res) + { + initFonts(1.f); + LLHUDText::reshape(); + } + + gDisplaySwapBuffers = TRUE; + + // Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding) + // Note: this formula depends on the number of components being 3. Not obvious, but it's correct. + image_width += (image_width * (type == SNAPSHOT_TYPE_DEPTH ? 4 : 3)) % 4; + + // Resize image + raw->scale( image_width, image_height ); + + setCursor(UI_CURSOR_ARROW); + + if (do_rebuild) + { + // If we had to do a rebuild, that means that the lists of drawables to be rendered + // was empty before we started. + // Need to reset these, otherwise we call state sort on it again when render gets called the next time + // and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of + // objects on them. + gPipeline.resetDrawOrders(); + } + + if (high_res) + { + send_agent_resume(); + } + + return TRUE; +} + +void LLViewerWindow::destroyWindow() +{ + if (mWindow) + { + LLWindowManager::destroyWindow(mWindow); + } + mWindow = NULL; +} + + +void LLViewerWindow::drawMouselookInstructions() +{ + // Draw instructions for mouselook ("Press ESC to leave Mouselook" in a box at the top of the screen.) + const char* instructions = "Press ESC to leave Mouselook."; + const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF ); + + const S32 INSTRUCTIONS_PAD = 5; + LLRect instructions_rect; + instructions_rect.setLeftTopAndSize( + INSTRUCTIONS_PAD, + gViewerWindow->getWindowHeight() - INSTRUCTIONS_PAD, + font->getWidth( instructions ) + 2 * INSTRUCTIONS_PAD, + llround(font->getLineHeight() + 2 * INSTRUCTIONS_PAD)); + + { + LLGLSNoTexture gls_no_texture; + glColor4f( 0.9f, 0.9f, 0.9f, 1.0f ); + gl_rect_2d( instructions_rect ); + } + + font->renderUTF8( + instructions, 0, + instructions_rect.mLeft + INSTRUCTIONS_PAD, + instructions_rect.mTop - INSTRUCTIONS_PAD, + LLColor4( 0.0f, 0.0f, 0.0f, 1.f ), + LLFontGL::LEFT, LLFontGL::TOP); +} + + +// These functions are here only because LLViewerWindow used to do the work that gFocusMgr does now. +// They let other objects continue to work without change. + +void LLViewerWindow::setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus)) +{ + gFocusMgr.setKeyboardFocus( new_focus, on_focus_lost ); +} + +LLUICtrl* LLViewerWindow::getKeyboardFocus() +{ + return gFocusMgr.getKeyboardFocus(); +} + +BOOL LLViewerWindow::hasKeyboardFocus(const LLUICtrl* possible_focus) const +{ + return possible_focus == gFocusMgr.getKeyboardFocus(); +} + +BOOL LLViewerWindow::childHasKeyboardFocus(const LLView* parent) const +{ + return gFocusMgr.childHasKeyboardFocus( parent ); +} + +void LLViewerWindow::setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor)) +{ + gFocusMgr.setMouseCapture( new_captor, on_capture_lost ); +} + +LLMouseHandler* LLViewerWindow::getMouseCaptor() const +{ + return gFocusMgr.getMouseCapture(); +} + +BOOL LLViewerWindow::hasMouseCapture(const LLMouseHandler* possible_captor) const +{ + return possible_captor == gFocusMgr.getMouseCapture(); +} + +S32 LLViewerWindow::getWindowHeight() const +{ + return mVirtualWindowRect.getHeight(); +} + +S32 LLViewerWindow::getWindowWidth() const +{ + return mVirtualWindowRect.getWidth(); +} + +S32 LLViewerWindow::getWindowDisplayHeight() const +{ + return mWindowRect.getHeight(); +} + +S32 LLViewerWindow::getWindowDisplayWidth() const +{ + return mWindowRect.getWidth(); +} + +LLView* LLViewerWindow::getTopView() const +{ + return gFocusMgr.getTopView(); +} + +BOOL LLViewerWindow::hasTopView(LLView* view) const +{ + return view == gFocusMgr.getTopView(); +} + +void LLViewerWindow::setTopView(LLView* new_top,void (*on_top_lost)(LLView* old_top)) +{ + gFocusMgr.setTopView( new_top, on_top_lost ); +} + +void LLViewerWindow::setupViewport(S32 x_offset, S32 y_offset) +{ + glViewport(x_offset, y_offset, mWindowRect.getWidth(), mWindowRect.getHeight()); +} + +void LLViewerWindow::setup3DRender() +{ + gCamera->setPerspective(NOT_FOR_SELECTION, 0, 0, mWindowRect.getWidth(), mWindowRect.getHeight(), FALSE, gCamera->getNear(), MAX_FAR_PLANE); +} + +void LLViewerWindow::setup2DRender() +{ + gl_state_for_2d(mWindowRect.getWidth(), mWindowRect.getHeight()); +} + +// Could cache the pointer from the last hitObjectOrLand here. +LLViewerObject *LLViewerWindow::lastObjectHit() +{ + return gObjectList.findObject( gLastHitObjectID ); +} + +const LLVector3d& LLViewerWindow::lastObjectHitOffset() +{ + return gLastHitObjectOffset; +} + +// Could cache the pointer from the last hitObjectOrLand here. +LLViewerObject *LLViewerWindow::lastNonFloraObjectHit() +{ + return gObjectList.findObject( gLastHitNonFloraObjectID ); +} + +const LLVector3d& LLViewerWindow::lastNonFloraObjectHitOffset() +{ + return gLastHitNonFloraObjectOffset; +} + + +void LLViewerWindow::setShowProgress(const BOOL show) +{ + if (mProgressView) + { + mProgressView->setVisible(show); + } +} + +BOOL LLViewerWindow::getShowProgress() const +{ + return (mProgressView && mProgressView->getVisible()); +} + + +void LLViewerWindow::moveProgressViewToFront() +{ + if( mProgressView && mRootView ) + { + mRootView->removeChild( mProgressView ); + mRootView->addChild( mProgressView ); + } +} + +void LLViewerWindow::setProgressString(const LLString& string) +{ + if (mProgressView) + { + mProgressView->setText(string); + } +} + +void LLViewerWindow::setProgressMessage(const LLString& msg) +{ + if(mProgressView) + { + mProgressView->setMessage(msg); + } +} + +void LLViewerWindow::setProgressPercent(const F32 percent) +{ + if (mProgressView) + { + mProgressView->setPercent(percent); + } +} + +void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const LLString& label ) +{ + if (mProgressView) + { + mProgressView->setCancelButtonVisible( b, label ); + } +} + + +LLProgressView *LLViewerWindow::getProgressView() const +{ + return mProgressView; +} + +void LLViewerWindow::dumpState() +{ + llinfos << "LLViewerWindow Active " << S32(mActive) << llendl; + llinfos << "mWindow visible " << S32(mWindow->getVisible()) + << " minimized " << S32(mWindow->getMinimized()) + << llendl; +} + +void LLViewerWindow::stopGL(BOOL save_state) +{ + if (!gGLManager.mIsDisabled) + { + llinfos << "Shutting down GL..." << llendl; + gSky.destroyGL(); + stop_glerror(); + + gImageList.destroyGL(save_state); + stop_glerror(); + + gBumpImageList.destroyGL(); + stop_glerror(); + + LLFontGL::destroyGL(); + stop_glerror(); + + LLVOAvatar::destroyGL(); + stop_glerror(); + + LLDynamicTexture::destroyGL(); + stop_glerror(); + + if(gParcelMgr) gParcelMgr->destroyGL(); + + gPipeline.destroyGL(); + + gCone.cleanupGL(); + gBox.cleanupGL(); + gSphere.cleanupGL(); + gCylinder.cleanupGL(); + + gGLManager.mIsDisabled = TRUE; + stop_glerror(); + + llinfos << "Remaining allocated texture memory: " << LLImageGL::sGlobalTextureMemory << " bytes" << llendl; + } +} + +void LLViewerWindow::restoreGL(const LLString& progress_message) +{ + if (gGLManager.mIsDisabled) + { + llinfos << "Restoring GL..." << llendl; + gGLManager.mIsDisabled = FALSE; + + // for future support of non-square pixels, and fonts that are properly stretched + //LLFontGL::destroyDefaultFonts(); + initFonts(); + initGLDefaults(); + LLGLState::restoreGL(); + gSky.restoreGL(); + gPipeline.restoreGL(); + LLDrawPoolWater::restoreGL(); + LLManipTranslate::restoreGL(); + gImageList.restoreGL(); + gBumpImageList.restoreGL(); + gPipeline.setUseAGP(gSavedSettings.getBOOL("RenderUseAGP")); + LLDynamicTexture::restoreGL(); + LLVOAvatar::restoreGL(); + if (gParcelMgr) gParcelMgr->restoreGL(); + + if (gFloaterCustomize && gFloaterCustomize->getVisible()) + { + LLVisualParamHint::requestHintUpdates(); + } + + if (!progress_message.empty()) + { + gRestoreGLTimer.reset(); + gRestoreGL = TRUE; + setShowProgress(TRUE); + setProgressString(progress_message); + } + llinfos << "...Restoring GL done" << llendl; +#if LL_WINDOWS + if (SetUnhandledExceptionFilter(LLWinDebug::handleException) != LLWinDebug::handleException) + { + llwarns << " Someone took over my exception handler (post restoreGL)!" << llendl; + } +#endif + + } +} + +void LLViewerWindow::initFonts(F32 zoom_factor) +{ + LLFontGL::destroyGL(); + LLFontGL::initDefaultFonts( gSavedSettings.getF32("FontScreenDPI"), + mDisplayScale.mV[VX] * zoom_factor, + mDisplayScale.mV[VY] * zoom_factor, + gSavedSettings.getString("FontMonospace"), + gSavedSettings.getF32("FontSizeMonospace"), + gSavedSettings.getString("FontSansSerif"), + gSavedSettings.getString("FontSansSerifFallback"), + gSavedSettings.getF32("FontSansSerifFallbackScale"), + gSavedSettings.getF32("FontSizeSmall"), + gSavedSettings.getF32("FontSizeMedium"), + gSavedSettings.getF32("FontSizeLarge"), + gSavedSettings.getF32("FontSizeHuge"), + gSavedSettings.getString("FontSansSerifBold"), + gSavedSettings.getF32("FontSizeMedium"), + gDirUtilp->getAppRODataDir() + ); +} +void LLViewerWindow::toggleFullscreen(BOOL show_progress) +{ + if (mWindow) + { + mWantFullscreen = mWindow->getFullscreen() ? FALSE : TRUE; + mShowFullscreenProgress = show_progress; + } +} + +void LLViewerWindow::getTargetWindow(BOOL& fullscreen, S32& width, S32& height) const +{ + fullscreen = mWantFullscreen; + + if (gViewerWindow->mWindow + && gViewerWindow->mWindow->getFullscreen() == mWantFullscreen) + { + width = gViewerWindow->getWindowDisplayWidth(); + height = gViewerWindow->getWindowDisplayHeight(); + } + else if (mWantFullscreen) + { + width = gSavedSettings.getS32("FullScreenWidth"); + height = gSavedSettings.getS32("FullScreenHeight"); + } + else + { + width = gSavedSettings.getS32("WindowWidth"); + height = gSavedSettings.getS32("WindowHeight"); + } +} + + +BOOL LLViewerWindow::checkSettings() +{ + BOOL is_fullscreen = gViewerWindow->mWindow->getFullscreen(); + if (is_fullscreen && !mWantFullscreen) + { + gViewerWindow->changeDisplaySettings(FALSE, + LLCoordScreen(gSavedSettings.getS32("WindowWidth"), + gSavedSettings.getS32("WindowHeight")), + TRUE, + mShowFullscreenProgress); + return TRUE; + } + else if (!is_fullscreen && mWantFullscreen) + { + if (!LLStartUp::canGoFullscreen()) + { + return FALSE; + } + + gViewerWindow->changeDisplaySettings(TRUE, + LLCoordScreen(gSavedSettings.getS32("FullScreenWidth"), + gSavedSettings.getS32("FullScreenHeight")), + gSavedSettings.getBOOL("DisableVerticalSync"), + mShowFullscreenProgress); + return TRUE; + } + return FALSE; +} + +void LLViewerWindow::restartDisplay(BOOL show_progress_bar) +{ + llinfos << "Restaring GL" << llendl; + stopGL(); + if (show_progress_bar) + { + restoreGL("Changing Resolution..."); + } + else + { + restoreGL(); + } +} + +BOOL LLViewerWindow::changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar) +{ + BOOL was_maximized = gSavedSettings.getBOOL("WindowMaximized"); + mWantFullscreen = fullscreen; + mShowFullscreenProgress = show_progress_bar; + gSavedSettings.setBOOL("FullScreen", mWantFullscreen); + + BOOL old_fullscreen = mWindow->getFullscreen(); + if (!old_fullscreen && fullscreen && !LLStartUp::canGoFullscreen()) + { + // we can't do this now, so do it later + + gSavedSettings.setS32("FullScreenWidth", size.mX); + gSavedSettings.setS32("FullScreenHeight", size.mY); + //gSavedSettings.setBOOL("DisableVerticalSync", disable_vsync); + + return TRUE; // a lie..., because we'll get to it later + } + + // going from windowed to windowed + if (!old_fullscreen && !fullscreen) + { + // if not maximized, use the request size + if (!mWindow->getMaximized()) + { + mWindow->setSize(size); + } + return TRUE; + } + + // Close floaters that don't handle settings change + LLFloaterSnapshot::hide(0); + + BOOL result_first_try = FALSE; + BOOL result_second_try = FALSE; + + LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus(); + send_agent_pause(); + llinfos << "Stopping GL during changeDisplaySettings" << llendl; + stopGL(); + mIgnoreActivate = TRUE; + LLCoordScreen old_size; + LLCoordScreen old_pos; + mWindow->getSize(&old_size); + BOOL got_position = mWindow->getPosition(&old_pos); + + if (!old_fullscreen && fullscreen && got_position) + { + // switching from windowed to fullscreen, so save window position + gSavedSettings.setS32("WindowX", old_pos.mX); + gSavedSettings.setS32("WindowY", old_pos.mY); + } + + result_first_try = mWindow->switchContext(fullscreen, size, disable_vsync); + if (!result_first_try) + { + // try to switch back + result_second_try = mWindow->switchContext(old_fullscreen, old_size, disable_vsync); + + if (!result_second_try) + { + // we are stuck...try once again with a minimal resolution? + send_agent_resume(); + mIgnoreActivate = FALSE; + return FALSE; + } + } + send_agent_resume(); + + llinfos << "Restoring GL during resolution change" << llendl; + if (show_progress_bar) + { + restoreGL("Changing Resolution..."); + } + else + { + restoreGL(); + } + + if (!result_first_try) + { + LLStringBase<char>::format_map_t args; + args["[RESX]"] = llformat("%d",size.mX); + args["[RESY]"] = llformat("%d",size.mY); + alertXml("ResolutionSwitchFail", args); + size = old_size; // for reshape below + } + + BOOL success = result_first_try || result_second_try; + if (success) + { +#if LL_WINDOWS + // Only trigger a reshape after switching to fullscreen; otherwise rely on the windows callback + // (otherwise size is wrong; this is the entire window size, reshape wants the visible window size) + if (fullscreen) +#endif + { + reshape(size.mX, size.mY); + } + } + + if (!mWindow->getFullscreen() && success) + { + // maximize window if was maximized, else reposition + if (was_maximized) + { + mWindow->maximize(); + } + else + { + S32 windowX = gSavedSettings.getS32("WindowX"); + S32 windowY = gSavedSettings.getS32("WindowY"); + + mWindow->setPosition(LLCoordScreen ( windowX, windowY ) ); + } + } + + mIgnoreActivate = FALSE; + gFocusMgr.setKeyboardFocus(keyboard_focus, NULL); + mWantFullscreen = mWindow->getFullscreen(); + mShowFullscreenProgress = FALSE; + + return success; +} + + +F32 LLViewerWindow::getDisplayAspectRatio() const +{ + if (mWindow->getFullscreen()) + { + if (gSavedSettings.getBOOL("FullScreenAutoDetectAspectRatio")) + { + return mWindow->getNativeAspectRatio(); + } + else + { + return gSavedSettings.getF32("FullScreenAspectRatio"); + } + } + else + { + return mWindow->getNativeAspectRatio(); + } +} + + +void LLViewerWindow::drawPickBuffer() const +{ + if (mPickBuffer) + { + LLGLDisable no_blend(GL_BLEND); + LLGLDisable no_alpha_test(GL_ALPHA_TEST); + LLGLSNoTexture no_texture; + glPixelZoom(10.f, 10.f); + glRasterPos2f(((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f), + ((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f)); + glDrawPixels(PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer); + glPixelZoom(1.f, 1.f); + glColor4fv(LLColor4::white.mV); + gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)), + llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)), + FALSE); + gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)), + llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f + 10.f)); + gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)), + llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f + 10.f), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f)); + glTranslatef(10.f, 10.f, 0.f); + gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f), + llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]), + FALSE); + gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX)* 10.f), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY + 1) * 10.f), + llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX + 1) * 10.f), + llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY) * 10.f), + FALSE); + glPopMatrix(); + } +} + +void LLViewerWindow::calcDisplayScale() +{ + F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); + LLVector2 display_scale; + display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); + F32 height_normalization = gSavedSettings.getBOOL("UIAutoScale") ? ((F32)mWindowRect.getHeight() / display_scale.mV[VY]) / 768.f : 1.f; + if(mWindow->getFullscreen()) + { + display_scale *= (ui_scale_factor * height_normalization); + } + else + { + display_scale *= ui_scale_factor; + } + + // limit minimum display scale + if (display_scale.mV[VX] < MIN_DISPLAY_SCALE || display_scale.mV[VY] < MIN_DISPLAY_SCALE) + { + display_scale *= MIN_DISPLAY_SCALE / llmin(display_scale.mV[VX], display_scale.mV[VY]); + } + + if (mWindow->getFullscreen()) + { + display_scale.mV[0] = llround(display_scale.mV[0], 2.0f/(F32) mWindowRect.getWidth()); + display_scale.mV[1] = llround(display_scale.mV[1], 2.0f/(F32) mWindowRect.getHeight()); + } + + if (display_scale != mDisplayScale) + { + llinfos << "Setting display scale to " << display_scale << llendl; + + mDisplayScale = display_scale; + // Init default fonts + initFonts(); + } +} + +//---------------------------------------------------------------------------- + +// static +bool LLViewerWindow::alertCallback(S32 modal) +{ + if (gNoRender) + { + return false; + } + else + { +// if (modal) // we really always want to take you out of mouselook + { + // If we're in mouselook, the mouse is hidden and so the user can't click + // the dialog buttons. In that case, change to First Person instead. + if( gAgent.cameraMouselook() ) + { + gAgent.changeCameraToDefault(); + } + } + return true; + } +} + +LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename, + LLAlertDialog::alert_callback_t callback, void* user_data) +{ + LLString::format_map_t args; + return alertXml( xml_filename, args, callback, user_data ); +} + +LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename, const LLString::format_map_t& args, + LLAlertDialog::alert_callback_t callback, void* user_data) +{ + if (gNoRender) + { + llinfos << "Alert: " << xml_filename << llendl; + if (callback) + { + callback(-1, user_data); + } + return NULL; + } + + // If we're in mouselook, the mouse is hidden and so the user can't click + // the dialog buttons. In that case, change to First Person instead. + if( gAgent.cameraMouselook() ) + { + gAgent.changeCameraToDefault(); + } + + // Note: object adds, removes, and destroys itself. + return LLAlertDialog::showXml( xml_filename, args, callback, user_data ); +} + +LLAlertDialog* LLViewerWindow::alertXmlEditText(const std::string& xml_filename, const LLString::format_map_t& args, + LLAlertDialog::alert_callback_t callback, void* user_data, + LLAlertDialog::alert_text_callback_t text_callback, void *text_data, + const LLString::format_map_t& edit_args, BOOL draw_asterixes) +{ + if (gNoRender) + { + llinfos << "Alert: " << xml_filename << llendl; + if (callback) + { + callback(-1, user_data); + } + return NULL; + } + + // If we're in mouselook, the mouse is hidden and so the user can't click + // the dialog buttons. In that case, change to First Person instead. + if( gAgent.cameraMouselook() ) + { + gAgent.changeCameraToDefault(); + } + + // Note: object adds, removes, and destroys itself. + LLAlertDialog* alert = LLAlertDialog::createXml( xml_filename, args, callback, user_data ); + if (alert) + { + if (text_callback) + { + alert->setEditTextCallback(text_callback, text_data); + } + alert->setEditTextArgs(edit_args); + alert->setDrawAsterixes(draw_asterixes); + alert->show(); + } + return alert; +} + +LLBottomPanel::LLBottomPanel(const LLString &name, const LLRect &rect) : + LLPanel(name, rect, FALSE), + mIndicator(NULL) +{ + // bottom panel is focus root, so Tab moves through the toolbar and button bar, and overlay + setFocusRoot(TRUE); + // don't capture mouse clicks that don't hit a child + setMouseOpaque(FALSE); + setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); + setIsChrome(TRUE); +} + +void LLBottomPanel::setFocusIndicator(LLView * indicator) +{ + mIndicator = indicator; +} + +void LLBottomPanel::draw() +{ + if(mIndicator) + { + BOOL hasFocus = gFocusMgr.childHasKeyboardFocus(this); + mIndicator->setVisible(hasFocus); + mIndicator->setEnabled(hasFocus); + } + LLPanel::draw(); +} |