summaryrefslogtreecommitdiff
path: root/indra/llwindow/llwindowsdl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llwindow/llwindowsdl.cpp')
-rw-r--r--indra/llwindow/llwindowsdl.cpp2487
1 files changed, 2487 insertions, 0 deletions
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
new file mode 100644
index 0000000000..75793eb739
--- /dev/null
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -0,0 +1,2487 @@
+/**
+ * @file llwindowsdl.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_SDL
+
+#include "linden_common.h"
+
+#include "llwindowsdl.h"
+#include "llkeyboardsdl.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llstring.h"
+#include "lldir.h"
+
+#include "llglheaders.h"
+
+#include "indra_constants.h"
+
+#if LL_GTK
+# include "gtk/gtk.h"
+#endif // LL_GTK
+
+#if LL_LINUX
+// not necessarily available on random SDL platforms, so #if LL_LINUX
+// for execv(), waitpid(), fork()
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif // LL_LINUX
+
+extern BOOL gDebugWindowProc;
+
+// culled from winuser.h
+//const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+// On the Mac, the scroll wheel reports a delta of 1 for each detent.
+// There's also acceleration for faster scrolling, based on a slider in the system preferences.
+const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+
+//
+// LLWindowSDL
+//
+
+#if LL_X11
+# include <X11/Xutil.h>
+// A global! Well, SDL isn't really designed for communicating
+// with multiple physical X11 displays. Heck, it's not really
+// designed for multiple X11 windows.
+// So, we need this for the SDL/X11 event filter callback (which
+// doesnt have a userdata parameter) and more.
+static Display *SDL_Display = NULL;
+static Window SDL_XWindowID = None;
+#endif //LL_X11
+
+// TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for the same reasons)
+// For SDL, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
+// The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
+// require a pointer to the LLWindowMacSDL object. Stash it here and maintain in the constructor and destructor.
+// This assumes that there will be only one object of this class at any time. Hopefully this is true.
+static LLWindowSDL *gWindowImplementation = NULL;
+
+static BOOL was_fullscreen = FALSE;
+
+// Cross-platform bits:
+
+void show_window_creation_error(const char* title)
+{
+ llwarns << title << llendl;
+ shell_open( "help/window_creation_error.html");
+ /*
+ OSMessageBox(
+ "Second Life is unable to run because it can't set up your display.\n"
+ "We need to be able to make a 32-bit color window at 1024x768, with\n"
+ "an 8 bit alpha channel.\n"
+ "\n"
+ "First, be sure your monitor is set to True Color (32-bit) in\n"
+ "Start -> Control Panels -> Display -> Settings.\n"
+ "\n"
+ "Otherwise, this may be due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "ATI drivers are available at http://www.ati.com/\n"
+ "nVidia drivers are available at http://www.nvidia.com/\n"
+ "\n"
+ "If you continue to receive this message, contact customer service.",
+ title,
+ OSMB_OK);
+ */
+}
+
+
+#if LL_GTK
+// Check the runtime GTK version for goodness.
+static BOOL maybe_do_gtk_diagnostics(void)
+{
+ static BOOL done_gtk_diag = FALSE;
+ static BOOL is_good = TRUE;
+ gtk_disable_setlocale();
+ if ((!done_gtk_diag) && gtk_init_check(NULL, NULL))
+ {
+ llinfos << "GTK Initialized." << llendl;
+ llinfos << "- Compiled against GTK version "
+ << GTK_MAJOR_VERSION << "."
+ << GTK_MINOR_VERSION << "."
+ << GTK_MICRO_VERSION << llendl;
+ llinfos << "- Running against GTK version "
+ << gtk_major_version << "."
+ << gtk_minor_version << "."
+ << gtk_micro_version << llendl;
+ gchar *gtk_warning;
+ gtk_warning = gtk_check_version(GTK_MAJOR_VERSION,
+ GTK_MINOR_VERSION,
+ GTK_MICRO_VERSION);
+ if (gtk_warning)
+ {
+ llwarns << "- GTK COMPATIBILITY WARNING: " <<
+ gtk_warning << llendl;
+ is_good = FALSE;
+ }
+
+ done_gtk_diag = TRUE;
+ }
+ return is_good;
+}
+#endif // LL_GTK
+
+
+BOOL check_for_card(const char* RENDERER, const char* bad_card)
+{
+ if (!strncasecmp(RENDERER, bad_card, strlen(bad_card)))
+ {
+ char buffer[1024];
+ sprintf(buffer,
+ "Your video card appears to be a %s, which Second Life does not support.\n"
+ "\n"
+ "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
+ "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
+ "and ATI Radeon 8500 or better.\n"
+ "\n"
+ "If you own a supported card and continue to receive this message, try \n"
+ "updating to the latest video card drivers. Otherwise look in the\n"
+ "secondlife.com support section or e-mail technical support\n"
+ "\n"
+ "You can try to run Second Life, but it will probably crash or run\n"
+ "very slowly. Try anyway?",
+ bad_card);
+ S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
+ if (OSBTN_YES == button)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+
+LLWindowSDL::LLWindowSDL(char *title, S32 x, S32 y, S32 width,
+ S32 height, U32 flags,
+ BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags), mGamma(1.0f)
+{
+ // Initialize the keyboard
+ gKeyboard = new LLKeyboardSDL();
+ // Note that we can't set up key-repeat until after SDL has init'd video
+
+ // Ignore use_gl for now, only used for drones on PC
+ mWindow = NULL;
+ mCursorDecoupled = FALSE;
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ mCursorIgnoreNextDelta = FALSE;
+ mNeedsResize = FALSE;
+ mOverrideAspectRatio = 0.f;
+ mGrabbyKeyFlags = 0;
+ mReallyCapturedCount = 0;
+ mHaveInputFocus = -1;
+ mIsMinimized = -1;
+
+ // Get the original aspect ratio of the main device.
+ mOriginalAspectRatio = 1024.0 / 768.0; // !!! FIXME //(double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
+
+ if (!title)
+ title = "SDL Window"; // !!! FIXME
+
+ // Stash the window title
+ mWindowTitle = new char[strlen(title) + 1];
+ strcpy(mWindowTitle, title);
+
+ // Create the GL context and set it up for windowed or fullscreen, as appropriate.
+ if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
+ {
+ gGLManager.initGL();
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+
+ stop_glerror();
+
+ // Stash an object pointer for OSMessageBox()
+ gWindowImplementation = this;
+
+#if LL_X11
+ mFlashing = FALSE;
+#endif // LL_X11
+}
+
+static SDL_Surface *Load_BMP_Resource(const char *basename)
+{
+ const int PATH_BUFFER_SIZE=1000;
+ char path_buffer[PATH_BUFFER_SIZE];
+
+ // Figure out where our BMP is living on the disk
+ snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",
+ gDirUtilp->getAppRODataDir().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ basename);
+ path_buffer[PATH_BUFFER_SIZE-1] = '\0';
+
+ return SDL_LoadBMP(path_buffer);
+}
+
+BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
+{
+ //bool glneedsinit = false;
+// const char *gllibname = null; // !!! fixme
+
+ llinfos << "createContext, fullscreen=" << fullscreen <<
+ " size=" << width << "x" << height << llendl;
+
+ // captures don't survive contexts
+ mGrabbyKeyFlags = 0;
+ mReallyCapturedCount = 0;
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ // !!! fixme: stderr?
+ llinfos << "sdl_init() failed! " << SDL_GetError() << llendl;
+ setupFailure("window creation error", "error", OSMB_OK);
+ return false;
+ }
+
+ SDL_version c_sdl_version;
+ SDL_VERSION(&c_sdl_version);
+ llinfos << "Compiled against SDL "
+ << int(c_sdl_version.major) << "."
+ << int(c_sdl_version.minor) << "."
+ << int(c_sdl_version.patch) << llendl;
+ const SDL_version *r_sdl_version;
+ r_sdl_version = SDL_Linked_Version();
+ llinfos << " Running against SDL "
+ << int(r_sdl_version->major) << "."
+ << int(r_sdl_version->minor) << "."
+ << int(r_sdl_version->patch) << llendl;
+
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo( );
+ if (!videoInfo)
+ {
+ llinfos << "SDL_GetVideoInfo() failed! " << SDL_GetError() << llendl;
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ SDL_EnableUNICODE(1);
+ SDL_WM_SetCaption(mWindowTitle, mWindowTitle);
+
+ // Set the application icon.
+ SDL_Surface *bmpsurface;
+ bmpsurface = Load_BMP_Resource("ll_icon.BMP");
+ if (bmpsurface)
+ {
+ // This attempts to give a black-keyed mask to the icon.
+ SDL_SetColorKey(bmpsurface,
+ SDL_SRCCOLORKEY,
+ SDL_MapRGB(bmpsurface->format, 0,0,0) );
+ SDL_WM_SetIcon(bmpsurface, NULL);
+ // The SDL examples cheerfully avoid freeing the icon
+ // surface, but I'm betting that's leaky.
+ SDL_FreeSurface(bmpsurface);
+ bmpsurface = NULL;
+ }
+
+ // note: these SetAttributes make Tom's 9600-on-AMD64 fail to
+ // get a visual, but it's broken anyway when it does, and without
+ // these SetAttributes we might easily get an avoidable substandard
+ // visual to work with on most other machines.
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
+
+ // !!! FIXME: try to toggle vsync here?
+
+ mFullscreen = fullscreen;
+ was_fullscreen = fullscreen;
+
+ int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ mSDLFlags = sdlflags;
+
+ if (mFullscreen)
+ {
+ llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
+
+ // If the requested width or height is 0, find the best default for the monitor.
+ if((width == 0) || (height == 0))
+ {
+ // Scan through the list of modes, looking for one which has:
+ // height between 700 and 800
+ // aspect ratio closest to the user's original mode
+ S32 resolutionCount = 0;
+ LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
+
+ if(resolutionList != NULL)
+ {
+ F32 closestAspect = 0;
+ U32 closestHeight = 0;
+ U32 closestWidth = 0;
+ int i;
+
+ llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
+
+ for(i=0; i < resolutionCount; i++)
+ {
+ F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
+
+ llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
+
+ if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
+ (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
+ {
+ llinfos << " (new closest mode) " << llendl;
+
+ // This is the closest mode we've seen yet.
+ closestWidth = resolutionList[i].mWidth;
+ closestHeight = resolutionList[i].mHeight;
+ closestAspect = aspect;
+ }
+ }
+
+ width = closestWidth;
+ height = closestHeight;
+ }
+ }
+
+ if((width == 0) || (height == 0))
+ {
+ // Mode search failed for some reason. Use the old-school default.
+ width = 1024;
+ height = 768;
+ }
+
+ mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
+
+ if (mWindow)
+ {
+ mFullscreen = TRUE;
+ was_fullscreen = TRUE;
+ mFullscreenWidth = mWindow->w;
+ mFullscreenHeight = mWindow->h;
+ mFullscreenBits = mWindow->format->BitsPerPixel;
+ mFullscreenRefresh = -1;
+
+ llinfos << "Running at " << mFullscreenWidth
+ << "x" << mFullscreenHeight
+ << "x" << mFullscreenBits
+ << " @ " << mFullscreenRefresh
+ << llendl;
+ }
+ else
+ {
+ llwarns << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << llendl;
+ // No fullscreen support
+ mFullscreen = FALSE;
+ was_fullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ char error[256];
+ sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
+ OSMessageBox(error, "Error", OSMB_OK);
+ }
+ }
+
+ if(!mFullscreen && (mWindow == NULL))
+ {
+ if (width == 0)
+ width = 1024;
+ if (height == 0)
+ width = 768;
+
+ llinfos << "createContext: creating window " << width << "x" << height << "x" << bits << llendl;
+ mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
+
+ if (!mWindow)
+ {
+ llwarns << "createContext: window creation failure. SDL: " << SDL_GetError() << llendl;
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+ } else if (!mFullscreen && (mWindow != NULL))
+ {
+ llinfos << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << llendl;
+ }
+
+ /*if (!load_all_glsyms(gllibname))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return FALSE;
+ }*/
+
+ gGLManager.mVRAM = videoInfo->video_mem / 1024;
+ if (gGLManager.mVRAM != 0)
+ {
+ llinfos << "Detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
+ }
+ // If VRAM is not detected, that is handled later
+
+#if 0 // !!! FIXME: all video cards suck under Linux. :)
+ // Since we just created the context, it needs to be set up.
+ glNeedsInit = TRUE;
+ if(glNeedsInit)
+ {
+ // Check for some explicitly unsupported cards.
+ const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+
+ const char* CARD_LIST[] =
+ { "RAGE 128",
+ "RIVA TNT2",
+ "Intel 810",
+ "3Dfx/Voodoo3",
+ "Radeon 7000",
+ "Radeon 7200",
+ "Radeon 7500",
+ "Radeon DDR",
+ "Radeon VE",
+ "GDI Generic" };
+ const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
+
+ // Future candidates:
+ // ProSavage/Twister
+ // SuperSavage
+
+ S32 i;
+ for (i = 0; i < CARD_COUNT; i++)
+ {
+ if (check_for_card(RENDERER, CARD_LIST[i]))
+ {
+ close();
+ shell_open( "help/unsupported_card.html" );
+ return FALSE;
+ }
+ }
+ }
+#endif
+
+ GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits;
+
+ glGetIntegerv(GL_RED_BITS, &redBits);
+ glGetIntegerv(GL_GREEN_BITS, &greenBits);
+ glGetIntegerv(GL_BLUE_BITS, &blueBits);
+ glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
+ glGetIntegerv(GL_DEPTH_BITS, &depthBits);
+ glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
+
+ llinfos << "GL buffer:" << llendl
+ llinfos << " Red Bits " << S32(redBits) << llendl
+ llinfos << " Green Bits " << S32(greenBits) << llendl
+ llinfos << " Blue Bits " << S32(blueBits) << llendl
+ llinfos << " Alpha Bits " << S32(alphaBits) << llendl
+ llinfos << " Depth Bits " << S32(depthBits) << llendl
+ llinfos << " Stencil Bits " << S32(stencilBits) << llendl;
+
+ GLint colorBits = redBits + greenBits + blueBits + alphaBits;
+ // fixme: actually, it's REALLY important for picking that we get at
+ // least 8 bits each of red,green,blue. Alpha we can be a bit more
+ // relaxed about if we have to.
+ if (colorBits < 32)
+ {
+ close();
+ setupFailure(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+#if 0 // !!! FIXME: we're going to brave it for now...
+ if (alphaBits < 8)
+ {
+ close();
+ setupFailure(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+#endif
+
+#if LL_X11
+ init_x11clipboard();
+#endif // LL_X11
+
+ // We need to do this here, once video is init'd
+ if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
+ SDL_DEFAULT_REPEAT_INTERVAL))
+ llwarns << "Couldn't enable key-repeat: " << SDL_GetError() <<llendl;
+
+ // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
+ return TRUE;
+}
+
+
+// changing fullscreen resolution, or switching between windowed and fullscreen mode.
+BOOL LLWindowSDL::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+{
+ const BOOL needsRebuild = TRUE; // Just nuke the context and start over.
+ BOOL result = true;
+
+ llinfos << "switchContext, fullscreen=" << fullscreen << llendl;
+ stop_glerror();
+ if(needsRebuild)
+ {
+ destroyContext();
+ result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
+ if (result)
+ {
+ gGLManager.initGL();
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+ }
+
+ stop_glerror();
+
+ return result;
+}
+
+void LLWindowSDL::destroyContext()
+{
+ llinfos << "destroyContext begins" << llendl;
+#if LL_X11
+ quit_x11clipboard();
+#endif // LL_X11
+
+ // Clean up remaining GL state before blowing away window
+ llinfos << "shutdownGL begins" << llendl;
+ gGLManager.shutdownGL();
+ llinfos << "SDL_QuitSS/VID begins" << llendl;
+ SDL_QuitSubSystem(SDL_INIT_VIDEO); // !!! !!! FIXME: this might be risky...
+ //unload_all_glsyms();
+
+ mWindow = NULL;
+}
+
+LLWindowSDL::~LLWindowSDL()
+{
+ quitCursors();
+ destroyContext();
+
+ if(mSupportedResolutions != NULL)
+ {
+ delete []mSupportedResolutions;
+ }
+
+ delete[] mWindowTitle;
+
+ gWindowImplementation = NULL;
+}
+
+
+void LLWindowSDL::show()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::hide()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::minimize()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::restore()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowSDL::close()
+{
+ // Is window is already closed?
+ // if (!mWindow)
+ // {
+ // return;
+ // }
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ destroyContext();
+}
+
+BOOL LLWindowSDL::isValid()
+{
+ return (mWindow != NULL);
+}
+
+BOOL LLWindowSDL::getVisible()
+{
+ BOOL result = FALSE;
+
+ // !!! FIXME: This isn't really right...
+ if (mWindow)
+ {
+ result = TRUE;
+ }
+
+ return(result);
+}
+
+BOOL LLWindowSDL::getMinimized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow && (1 == mIsMinimized))
+ {
+ result = TRUE;
+ }
+ return(result);
+}
+
+BOOL LLWindowSDL::getMaximized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow)
+ {
+ // TODO
+ }
+
+ return(result);
+}
+
+BOOL LLWindowSDL::maximize()
+{
+ // TODO
+ return FALSE;
+}
+
+BOOL LLWindowSDL::getFullscreen()
+{
+ return mFullscreen;
+}
+
+BOOL LLWindowSDL::getPosition(LLCoordScreen *position)
+{
+ // !!! FIXME: can anything be done with this?
+ position->mX = 0;
+ position->mY = 0;
+ return TRUE;
+}
+
+BOOL LLWindowSDL::getSize(LLCoordScreen *size)
+{
+ if (mWindow)
+ {
+ size->mX = mWindow->w;
+ size->mY = mWindow->h;
+ return (TRUE);
+ }
+
+ llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
+ return (FALSE);
+}
+
+BOOL LLWindowSDL::getSize(LLCoordWindow *size)
+{
+ if (mWindow)
+ {
+ size->mX = mWindow->w;
+ size->mY = mWindow->h;
+ return (TRUE);
+ }
+
+ llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
+ return (FALSE);
+}
+
+BOOL LLWindowSDL::setPosition(const LLCoordScreen position)
+{
+ if(mWindow)
+ {
+ // !!! FIXME...
+ //MacMoveWindow(mWindow, position.mX, position.mY, false);
+ }
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::setSize(const LLCoordScreen size)
+{
+ if(mWindow)
+ {
+ // !!! FIXME...
+ //SizeWindow(mWindow, size.mX, size.mY, true);
+ }
+
+ return TRUE;
+}
+
+void LLWindowSDL::swapBuffers()
+{
+ if (mWindow)
+ SDL_GL_SwapBuffers();
+}
+
+F32 LLWindowSDL::getGamma()
+{
+ return 1/mGamma;
+}
+
+BOOL LLWindowSDL::restoreGamma()
+{
+ //CGDisplayRestoreColorSyncSettings();
+ SDL_SetGamma(1.0f, 1.0f, 1.0f);
+ return true;
+}
+
+BOOL LLWindowSDL::setGamma(const F32 gamma)
+{
+ mGamma = gamma;
+ if (mGamma == 0) mGamma = 0.1f;
+ mGamma = 1/mGamma;
+ SDL_SetGamma(mGamma, mGamma, mGamma);
+ return true;
+}
+
+BOOL LLWindowSDL::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+
+// Constrains the mouse to the window.
+void LLWindowSDL::setMouseClipping( BOOL b )
+{
+ //llinfos << "LLWindowSDL::setMouseClipping " << b << llendl;
+ // Just stash the requested state. We'll simulate this when the cursor is hidden by decoupling.
+ mIsMouseClipping = b;
+ //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
+ adjustCursorDecouple();
+}
+
+BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position)
+{
+ BOOL result = TRUE;
+ LLCoordScreen screen_pos;
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ //llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
+
+ SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
+
+ // Under certain circumstances, this will trigger us to decouple the cursor.
+ adjustCursorDecouple(true);
+
+ return result;
+}
+
+BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position)
+{
+ //Point cursor_point;
+ LLCoordScreen screen_pos;
+
+ //GetMouse(&cursor_point);
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+
+ screen_pos.mX = x;
+ screen_pos.mY = y;
+
+ return convertCoords(screen_pos, position);
+}
+
+void LLWindowSDL::adjustCursorDecouple(bool warpingMouse)
+{
+ if(mIsMouseClipping && mCursorHidden)
+ {
+ if(warpingMouse)
+ {
+ // The cursor should be decoupled. Make sure it is.
+ if(!mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
+ //CGAssociateMouseAndMouseCursorPosition(false);
+ mCursorDecoupled = true;
+ mCursorIgnoreNextDelta = TRUE;
+ }
+ }
+ }
+ else
+ {
+ // The cursor should not be decoupled. Make sure it isn't.
+ if(mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
+ //CGAssociateMouseAndMouseCursorPosition(true);
+ mCursorDecoupled = false;
+ }
+ }
+}
+
+F32 LLWindowSDL::getNativeAspectRatio()
+{
+#if 0
+ // RN: this hack presumes that the largest supported resolution is monitor-limited
+ // and that pixels in that mode are square, therefore defining the native aspect ratio
+ // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
+ S32 num_resolutions;
+ LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
+
+
+ return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
+ //rn: AC
+#endif
+
+ // MBW -- there are a couple of bad assumptions here. One is that the display list won't include
+ // ridiculous resolutions nobody would ever use. The other is that the list is in order.
+
+ // New assumptions:
+ // - pixels are square (the only reasonable choice, really)
+ // - The user runs their display at a native resolution, so the resolution of the display
+ // when the app is launched has an aspect ratio that matches the monitor.
+
+ //RN: actually, the assumption that there are no ridiculous resolutions (above the display's native capabilities) has
+ // been born out in my experience.
+ // Pixels are often not square (just ask the people who run their LCDs at 1024x768 or 800x600 when running fullscreen, like me)
+ // The ordering of display list is a blind assumption though, so we should check for max values
+ // Things might be different on the Mac though, so I'll defer to MBW
+
+ // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
+ // switching, and stashes it in mOriginalAspectRatio. Here, we just return it.
+
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+
+ return mOriginalAspectRatio;
+}
+
+F32 LLWindowSDL::getPixelAspectRatio()
+{
+ F32 pixel_aspect = 1.f;
+ if (getFullscreen())
+ {
+ LLCoordScreen screen_size;
+ getSize(&screen_size);
+ pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
+ }
+
+ return pixel_aspect;
+}
+
+
+// some of this stuff is to support 'temporarily windowed' mode so that
+// dialogs are still usable in fullscreen. HOWEVER! - it's not enabled/working
+// yet.
+static LLCoordScreen old_size;
+static BOOL old_fullscreen;
+void LLWindowSDL::beforeDialog()
+{
+ llinfos << "LLWindowSDL::beforeDialog()" << llendl;
+
+ if (SDLReallyCaptureInput(FALSE) // must ungrab input so popup works!
+ && getSize(&old_size))
+ {
+ old_fullscreen = was_fullscreen;
+
+ if (old_fullscreen)
+ {
+ // NOT YET WORKING
+ //switchContext(FALSE, old_size, TRUE);
+ }
+ }
+
+#if LL_X11
+ if (SDL_Display)
+ {
+ // Everything that we/SDL asked for should happen before we
+ // potentially hand control over to GTK.
+ XSync(SDL_Display, False);
+ }
+#endif // LL_X11
+
+#if LL_GTK
+ // this is a good time to grab some GTK version information for
+ // diagnostics
+ maybe_do_gtk_diagnostics();
+#endif // LL_GTK
+}
+
+void LLWindowSDL::afterDialog()
+{
+ llinfos << "LLWindowSDL::afterDialog()" << llendl;
+ if (old_fullscreen && !was_fullscreen)
+ {
+ // NOT YET WORKING (see below)
+ //switchContext(TRUE, old_size, TRUE);
+ }
+ // !!! FIXME - we need to restore the GL context using
+ // LLViewerWindow::restoreGL() - but how??
+}
+
+
+S32 LLWindowSDL::stat(const char* file_name, struct stat* stat_info)
+{
+ return ::stat( file_name, stat_info );
+}
+
+#if LL_X11
+// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
+void LLWindowSDL::x11_set_urgent(BOOL urgent)
+{
+ if (SDL_Display && !mFullscreen)
+ {
+ XWMHints *wm_hints;
+
+ llinfos << "X11 hint for urgency, " << urgent << llendl;
+
+ wm_hints = XGetWMHints(SDL_Display, mSDL_XWindowID);
+ if (!wm_hints)
+ wm_hints = XAllocWMHints();
+
+ if (urgent)
+ wm_hints->flags |= XUrgencyHint;
+ else
+ wm_hints->flags &= ~XUrgencyHint;
+
+ XSetWMHints(SDL_Display, mSDL_XWindowID, wm_hints);
+ XFree(wm_hints);
+ XSync(SDL_Display, False);
+ }
+}
+#endif // LL_X11
+
+void LLWindowSDL::flashIcon(F32 seconds)
+{
+#if !LL_X11
+ llinfos << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
+#else
+ llinfos << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
+
+ F32 remaining_time = mFlashTimer.getRemainingTimeF32();
+ if (remaining_time < seconds)
+ remaining_time = seconds;
+ mFlashTimer.reset();
+ mFlashTimer.setTimerExpirySec(remaining_time);
+
+ x11_set_urgent(TRUE);
+ mFlashing = TRUE;
+#endif // LL_X11
+}
+
+#if LL_X11
+/* Lots of low-level X11 stuff to handle X11 copy-and-paste */
+
+/* Our X11 clipboard support is a bit bizarre in various
+ organically-grown ways. Ideally it should be fixed to do
+ real string-type negotiation (this would make pasting to
+ xterm faster and pasting to UTF-8 emacs work properly), but
+ right now it has the rare and desirable trait of being
+ generally stable and working. */
+
+/* PRIMARY and CLIPBOARD are the two main kinds of
+ X11 clipboard. A third are the CUT_BUFFERs which an
+ obsolete holdover from X10 days and use a quite orthogonal
+ mechanism. CLIPBOARD is the type whose design most
+ closely matches SL's own win32-alike explicit copy-and-paste
+ paradigm.
+
+ Pragmatically we support all three to varying degrees. When
+ we paste into SL, it is strictly from CLIPBOARD. When we copy,
+ we support (to as full an extent as the clipboard content type
+ allows) CLIPBOARD, PRIMARY, and CUT_BUFFER0.
+ */
+#define SL_READWRITE_XCLIPBOARD_TYPE XInternAtom(SDL_Display, "CLIPBOARD", False)
+#define SL_WRITE_XCLIPBOARD_TYPE XA_PRIMARY
+
+/* This is where our own private cutbuffer goes - we don't use
+ a regular cutbuffer (XA_CUT_BUFFER0 etc) for intermediate
+ storage because their use isn't really defined for holding UTF8. */
+#define SL_CUTBUFFER_TYPE XInternAtom(SDL_Display, "SECONDLIFE_CUTBUFFER", False)
+
+/* These defines, and convert_data/convert_x11clipboard,
+ mostly exist to support non-text or unusually-encoded
+ clipboard data, which we don't really have a need for at
+ the moment. */
+#define SDLCLIPTYPE(A, B, C, D) (int)((A<<24)|(B<<16)|(C<<8)|(D<<0))
+#define FORMAT_PREFIX "SECONDLIFE_x11clipboard_0x"
+
+typedef Atom x11clipboard_type;
+
+static
+x11clipboard_type convert_format(int type)
+{
+ switch (type)
+ {
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ // old-style X11 clipboard, strictly only ISO 8859-1 encoding
+ return XA_STRING;
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ // newer de-facto UTF8 clipboard atom
+ return XInternAtom(SDL_Display, "UTF8_STRING", False);
+ default:
+ {
+ /* completely arbitrary clipboard types... we don't actually use
+ these right now, and support is skeletal. */
+ char format[sizeof(FORMAT_PREFIX)+8+1];
+
+ sprintf(format, "%s%08lx", FORMAT_PREFIX, (unsigned long)type);
+ return XInternAtom(SDL_Display, format, False);
+ }
+ }
+}
+
+/* convert platform string to x11 clipboard format. for our
+ purposes this is pretty trivial right now. */
+static int
+convert_data(int type, char *dst, const char *src, int srclen)
+{
+ int dstlen;
+
+ dstlen = 0;
+ switch (type)
+ {
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ if ( srclen == 0 )
+ srclen = strlen(src);
+
+ dstlen = srclen + 1;
+
+ if ( dst ) // assume caller made it big enough by asking us
+ {
+ memcpy(dst, src, srclen);
+ dst[srclen] = '\0';
+ }
+ break;
+
+ default:
+ llwarns << "convert_data: Unknown medium type" << llendl;
+ break;
+ }
+ return(dstlen);
+}
+
+/* Convert x11clipboard data to platform string. This too is
+ pretty trivial for our needs right now, and just about identical
+ to above. */
+static int
+convert_x11clipboard(int type, char *dst, const char *src, int srclen)
+{
+ int dstlen;
+
+ dstlen = 0;
+ switch (type)
+ {
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ if ( srclen == 0 )
+ srclen = strlen(src);
+
+ dstlen = srclen + 1;
+
+ if ( dst ) // assume caller made it big enough by asking us
+ {
+ memcpy(dst, src, srclen);
+ dst[srclen] = '\0';
+ }
+ break;
+
+ default:
+ llwarns << "convert_x11clipboard: Unknown medium type" << llendl;
+ break;
+ }
+ return dstlen;
+}
+
+int
+LLWindowSDL::is_empty_x11clipboard(void)
+{
+ int retval;
+
+ Lock_Display();
+ retval = ( XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE) == None );
+ Unlock_Display();
+
+ return(retval);
+}
+
+void
+LLWindowSDL::put_x11clipboard(int type, int srclen, const char *src)
+{
+ x11clipboard_type format;
+ int dstlen;
+ char *dst;
+
+ format = convert_format(type);
+ dstlen = convert_data(type, NULL, src, srclen);
+
+ dst = (char *)malloc(dstlen);
+ if ( dst != NULL )
+ {
+ Window root = DefaultRootWindow(SDL_Display);
+ Lock_Display();
+ convert_data(type, dst, src, srclen);
+ // Cutbuffers are only allowed to have STRING atom types,
+ // but Emacs puts UTF8 inside them anyway. We cautiously
+ // don't.
+ if (type == SDLCLIPTYPE('T','E','X','T'))
+ {
+ // dstlen-1 so we don't include the trailing \0
+ llinfos << "X11: Populating cutbuffer." <<llendl;
+ XChangeProperty(SDL_Display, root,
+ XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace,
+ (unsigned char*)dst, dstlen-1);
+ } else {
+ // Should we clear the cutbuffer if we can't put the selection in
+ // it because it's a UTF8 selection? Eh, no great reason I think.
+ //XDeleteProperty(SDL_Display, root, XA_CUT_BUFFER0);
+ }
+ // Private cutbuffer of an appropriate type.
+ XChangeProperty(SDL_Display, root,
+ SL_CUTBUFFER_TYPE, format, 8, PropModeReplace,
+ (unsigned char*)dst, dstlen-1);
+ free(dst);
+
+ /* Claim ownership of both PRIMARY and CLIPBOARD */
+ XSetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE,
+ mSDL_XWindowID, CurrentTime);
+ XSetSelectionOwner(SDL_Display, SL_WRITE_XCLIPBOARD_TYPE,
+ mSDL_XWindowID, CurrentTime);
+
+ Unlock_Display();
+ }
+}
+
+void
+LLWindowSDL::get_x11clipboard(int type, int *dstlen, char **dst)
+{
+ x11clipboard_type format;
+
+ *dstlen = 0;
+ format = convert_format(type);
+
+ Window owner;
+ Atom selection;
+ Atom seln_type;
+ int seln_format;
+ unsigned long nbytes;
+ unsigned long overflow;
+ char *src;
+
+ Lock_Display();
+ owner = XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE);
+ Unlock_Display();
+ if (owner == None)
+ {
+ // Fall right back to ancient X10 cut-buffers
+ owner = DefaultRootWindow(SDL_Display);
+ selection = XA_CUT_BUFFER0;
+ } else if (owner == mSDL_XWindowID)
+ {
+ // Use our own uncooked opaque string property
+ owner = DefaultRootWindow(SDL_Display);
+ selection = SL_CUTBUFFER_TYPE;
+ }
+ else
+ {
+ // Use full-on X11-style clipboard negotiation with the owning app
+ int selection_response = 0;
+ SDL_Event event;
+
+ owner = mSDL_XWindowID;
+ Lock_Display();
+ selection = XInternAtom(SDL_Display, "SDL_SELECTION", False);
+ XConvertSelection(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE, format,
+ selection, owner, CurrentTime);
+ Unlock_Display();
+ llinfos << "X11: Waiting for clipboard to arrive." <<llendl;
+ while ( ! selection_response )
+ {
+ // Only look for SYSWMEVENTs, or we may lose keypresses
+ // etc.
+ SDL_PumpEvents();
+ if (1 == SDL_PeepEvents(&event, 1, SDL_GETEVENT,
+ SDL_SYSWMEVENTMASK) )
+ {
+ if ( event.type == SDL_SYSWMEVENT )
+ {
+ XEvent xevent =
+ event.syswm.msg->event.xevent;
+
+ if ( (xevent.type == SelectionNotify)&&
+ (xevent.xselection.requestor == owner) )
+ selection_response = 1;
+ }
+ } else {
+ llinfos << "X11: Waiting for SYSWM event..." << llendl;
+ }
+ }
+ llinfos << "X11: Clipboard arrived." <<llendl;
+ }
+
+ Lock_Display();
+ if ( XGetWindowProperty(SDL_Display, owner, selection, 0, INT_MAX/4,
+ False, format, &seln_type, &seln_format,
+ &nbytes, &overflow, (unsigned char **)&src) == Success )
+ {
+ if ( seln_type == format )
+ {
+ *dstlen = convert_x11clipboard(type, NULL, src, nbytes);
+ *dst = (char *)realloc(*dst, *dstlen);
+ if ( *dst == NULL )
+ *dstlen = 0;
+ else
+ convert_x11clipboard(type, *dst, src, nbytes);
+ }
+ XFree(src);
+ }
+
+ Unlock_Display();
+}
+
+int clipboard_filter_callback(const SDL_Event *event)
+{
+ /* Post all non-window manager specific events */
+ if ( event->type != SDL_SYSWMEVENT )
+ {
+ return(1);
+ }
+
+ /* Handle window-manager specific clipboard events */
+ switch (event->syswm.msg->event.xevent.type) {
+ /* Copy the selection from SL_CUTBUFFER_TYPE to the requested property */
+ case SelectionRequest: {
+ XSelectionRequestEvent *req;
+ XEvent sevent;
+ int seln_format;
+ unsigned long nbytes;
+ unsigned long overflow;
+ unsigned char *seln_data;
+
+ req = &event->syswm.msg->event.xevent.xselectionrequest;
+ sevent.xselection.type = SelectionNotify;
+ sevent.xselection.display = req->display;
+ sevent.xselection.selection = req->selection;
+ sevent.xselection.target = None;
+ sevent.xselection.property = None;
+ sevent.xselection.requestor = req->requestor;
+ sevent.xselection.time = req->time;
+ if ( XGetWindowProperty(SDL_Display, DefaultRootWindow(SDL_Display),
+ SL_CUTBUFFER_TYPE, 0, INT_MAX/4, False, req->target,
+ &sevent.xselection.target, &seln_format,
+ &nbytes, &overflow, &seln_data) == Success )
+ {
+ if ( sevent.xselection.target == req->target)
+ {
+ if ( sevent.xselection.target == XA_STRING ||
+ sevent.xselection.target ==
+ convert_format(SDLCLIPTYPE('U','T','F','8')) )
+ {
+ if ( seln_data[nbytes-1] == '\0' )
+ --nbytes;
+ }
+ XChangeProperty(SDL_Display, req->requestor, req->property,
+ req->target, seln_format, PropModeReplace,
+ seln_data, nbytes);
+ sevent.xselection.property = req->property;
+#define XA_TARGETS XInternAtom(SDL_Display, "TARGETS", False)
+ } else if (XA_TARGETS == req->target) {
+ /* only advertise what we currently support */
+ const int num_supported = 3;
+ Atom supported[num_supported] = {
+ XA_STRING, // will be over-written below
+ XInternAtom(SDL_Display, "TEXT",False),
+ XA_TARGETS
+ };
+ supported[0] = sevent.xselection.target;
+ XChangeProperty(SDL_Display, req->requestor,
+ req->property, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)supported,
+ num_supported);
+ sevent.xselection.property = req->property;
+ llinfos << "Clipboard: An app asked us what selections format we offer." << llendl;
+ } else {
+ llinfos << "Clipboard: An app requested an unsupported selection format " << req->target << ", we have " << sevent.xselection.target << llendl;
+ sevent.xselection.target = None;
+ }
+ XFree(seln_data);
+ }
+ int sendret =
+ XSendEvent(SDL_Display,req->requestor,False,0,&sevent);
+ if ((sendret==BadValue) || (sendret==BadWindow))
+ llwarns << "Clipboard SendEvent failed" << llendl;
+ XSync(SDL_Display, False);
+ }
+ break;
+ }
+
+ /* Post the event for X11 clipboard reading above */
+ return(1);
+}
+
+int
+LLWindowSDL::init_x11clipboard(void)
+{
+ SDL_SysWMinfo info;
+ int retval;
+
+ /* Grab the window manager specific information */
+ retval = -1;
+ SDL_SetError("SDL is not running on known window manager");
+
+ SDL_VERSION(&info.version);
+ if ( SDL_GetWMInfo(&info) )
+ {
+ /* Save the information for later use */
+ if ( info.subsystem == SDL_SYSWM_X11 )
+ {
+ SDL_Display = info.info.x11.display;
+ SDL_XWindowID = info.info.x11.wmwindow;
+ mSDL_XWindowID = info.info.x11.wmwindow;
+ Lock_Display = info.info.x11.lock_func;
+ Unlock_Display = info.info.x11.unlock_func;
+
+ /* Enable the special window hook events */
+ SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
+ SDL_SetEventFilter(clipboard_filter_callback);
+
+ retval = 0;
+ }
+ else
+ {
+ SDL_SetError("SDL is not running on X11");
+ }
+ }
+ return(retval);
+}
+
+void
+LLWindowSDL::quit_x11clipboard(void)
+{
+ SDL_Display = NULL;
+ SDL_XWindowID = None;
+ mSDL_XWindowID = None;
+ Lock_Display = NULL;
+ Unlock_Display = NULL;
+
+ SDL_SetEventFilter(NULL); // Stop custom event filtering
+}
+
+/************************************************/
+
+BOOL LLWindowSDL::isClipboardTextAvailable()
+{
+ return !is_empty_x11clipboard();
+}
+
+BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
+{
+ int cliplen; // seems 1 or 2 bytes longer than expected
+ char *cliptext = NULL;
+ get_x11clipboard(SDLCLIPTYPE('U','T','F','8'), &cliplen, &cliptext);
+ if (cliptext)
+ {
+ llinfos << "X11: Got UTF8 clipboard text." << llendl;
+ // at some future time we can use cliplen instead of relying on \0,
+ // if we ever grok non-ascii, non-utf8 encodings on the clipboard.
+ std::string clip_str(cliptext);
+ // we can't necessarily trust the incoming text to be valid UTF-8,
+ // but utf8str_to_wstring() seems to do an appropriate level of
+ // validation for avoiding over-reads.
+ dst = utf8str_to_wstring(clip_str);
+ /*llinfos << "X11 pasteTextFromClipboard: cliplen=" << cliplen <<
+ " strlen(cliptext)=" << strlen(cliptext) <<
+ " clip_str.length()=" << clip_str.length() <<
+ " dst.length()=" << dst.length() <<
+ llendl;*/
+ free(cliptext);
+ return TRUE; // success
+ }
+ get_x11clipboard(SDLCLIPTYPE('T','E','X','T'), &cliplen, &cliptext);
+ if (cliptext)
+ {
+ llinfos << "X11: Got ISO 8859-1 clipboard text." << llendl;
+ std::string clip_str(cliptext);
+ std::string utf8_str = rawstr_to_utf8(clip_str);
+ dst = utf8str_to_wstring(utf8_str);
+ free(cliptext);
+ }
+ return FALSE; // failure
+}
+
+BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
+{
+ std::string utf8text = wstring_to_utf8str(s);
+ const char* cstr = utf8text.c_str();
+ int cstrlen = strlen(cstr);
+ int i;
+ for (i=0; i<cstrlen; ++i)
+ {
+ if (0x80 & (unsigned char)cstr[i])
+ {
+ // Found an 8-bit character; use new-style UTF8 clipboard
+ llinfos << "X11: UTF8 copyTextToClipboard" << llendl;
+ put_x11clipboard(SDLCLIPTYPE('U','T','F','8'), cstrlen, cstr);
+ return TRUE;
+ }
+ }
+ // Didn't find any 8-bit characters; use old-style ISO 8859-1 clipboard
+ llinfos << "X11: ISO 8859-1 copyTextToClipboard" << llendl;
+ put_x11clipboard(SDLCLIPTYPE('T','E','X','T'), cstrlen, cstr);
+ return TRUE;
+}
+#else
+
+BOOL LLWindowSDL::isClipboardTextAvailable()
+{
+ return FALSE; // unsupported
+}
+
+BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
+{
+ return FALSE; // unsupported
+}
+
+BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
+{
+ return FALSE; // unsupported
+}
+#endif // LL_X11
+
+BOOL LLWindowSDL::sendEmail(const char* address, const char* subject, const char* body_text,
+ const char* attachment, const char* attachment_displayed_name )
+{
+ // MBW -- XXX -- Um... yeah. I'll get to this later.
+
+ return FALSE;
+}
+
+
+LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ mNumSupportedResolutions = 0;
+
+ SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
+ if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
+ {
+ int count = 0;
+ while (*modes) // they're sorted biggest to smallest, so find end...
+ {
+ modes++;
+ count++;
+ }
+
+ while (count--)
+ {
+ modes--;
+ SDL_Rect *r = *modes;
+ int w = r->w;
+ int h = r->h;
+ if ((w >= 800) && (h >= 600))
+ {
+ // make sure we don't add the same resolution multiple times!
+ if ( (mNumSupportedResolutions == 0) ||
+ ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
+ (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ if (!to)
+ return FALSE;
+
+ to->mX = from.mX;
+ to->mY = mWindow->h - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ if (!to)
+ return FALSE;
+
+ to->mX = from.mX;
+ to->mY = mWindow->h - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ if (!to)
+ return FALSE;
+
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return (TRUE);
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ if (!to)
+ return FALSE;
+
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return (TRUE);
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+
+
+
+void LLWindowSDL::setupFailure(const char* text, const char* caption, U32 type)
+{
+ destroyContext();
+
+ OSMessageBox(text, caption, type);
+}
+
+BOOL LLWindowSDL::SDLReallyCaptureInput(BOOL capture)
+{
+ // note: this used to be safe to call nestedly, but in the
+ // end that's not really a wise usage pattern, so don't.
+
+ if (capture)
+ mReallyCapturedCount = 1;
+ else
+ mReallyCapturedCount = 0;
+
+ SDL_GrabMode wantmode, newmode;
+ if (mReallyCapturedCount <= 0) // uncapture
+ {
+ wantmode = SDL_GRAB_OFF;
+ } else // capture
+ {
+ wantmode = SDL_GRAB_ON;
+ }
+
+ if (mReallyCapturedCount < 0) // yuck, imbalance.
+ {
+ mReallyCapturedCount = 0;
+ llwarns << "ReallyCapture count was < 0" << llendl;
+ }
+
+ if (!mFullscreen) /* only bother if we're windowed anyway */
+ {
+#if LL_X11
+ if (SDL_Display)
+ {
+ /* we dirtily mix raw X11 with SDL so that our pointer
+ isn't (as often) constrained to the limits of the
+ window while grabbed, which feels nicer and
+ hopefully eliminates some reported 'sticky pointer'
+ problems. We use raw X11 instead of
+ SDL_WM_GrabInput() because the latter constrains
+ the pointer to the window and also steals all
+ *keyboard* input from the window manager, which was
+ frustrating users. */
+ int result;
+ if (wantmode == SDL_GRAB_ON)
+ {
+ //llinfos << "X11 POINTER GRABBY" << llendl;
+ //newmode = SDL_WM_GrabInput(wantmode);
+ result = XGrabPointer(SDL_Display, mSDL_XWindowID,
+ True, 0, GrabModeAsync,
+ GrabModeAsync,
+ None, None, CurrentTime);
+ if (GrabSuccess == result)
+ newmode = SDL_GRAB_ON;
+ else
+ newmode = SDL_GRAB_OFF;
+ } else if (wantmode == SDL_GRAB_OFF)
+ {
+ //llinfos << "X11 POINTER UNGRABBY" << llendl;
+ newmode = SDL_GRAB_OFF;
+ //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
+
+ XUngrabPointer(SDL_Display, CurrentTime);
+ // Make sure the ungrab happens RIGHT NOW.
+ XSync(SDL_Display, False);
+ } else
+ {
+ newmode = SDL_GRAB_QUERY; // neutral
+ }
+ } else // not actually running on X11, for some reason
+ newmode = wantmode;
+#endif // LL_X11
+ } else {
+ // pretend we got what we wanted, when really we don't care.
+ newmode = wantmode;
+ }
+
+ // return boolean success for whether we ended up in the desired state
+ return (capture && SDL_GRAB_ON==newmode) ||
+ (!capture && SDL_GRAB_OFF==newmode);
+}
+
+U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain)
+{
+ /* part of the fix for SL-13243: Some popular window managers like
+ to totally eat alt-drag for the purposes of moving windows. We
+ spoil their day by acquiring the exclusive X11 mouse lock for as
+ long as LALT is held down, so the window manager can't easily
+ see what's happening. Tested successfully with Metacity.
+ And... do the same with CTRL, for other darn WMs. We don't
+ care about other metakeys as SL doesn't use them with dragging
+ (for now). */
+
+ /* We maintain a bitmap of critical keys which are up and down
+ instead of simply key-counting, because SDL sometimes reports
+ misbalanced keyup/keydown event pairs to us for whatever reason. */
+
+ U32 mask = 0;
+ switch (keysym)
+ {
+ case SDLK_LALT:
+ mask = 1U << 0; break;
+ case SDLK_LCTRL:
+ mask = 1U << 1; break;
+ case SDLK_RCTRL:
+ mask = 1U << 2; break;
+ default:
+ break;
+ }
+
+ if (gain)
+ mGrabbyKeyFlags |= mask;
+ else
+ mGrabbyKeyFlags &= ~mask;
+
+ //llinfos << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << llendl;
+
+ /* 0 means we don't need to mousegrab, otherwise grab. */
+ return mGrabbyKeyFlags;
+}
+
+void LLWindowSDL::gatherInput()
+{
+ const Uint32 CLICK_THRESHOLD = 300; // milliseconds
+ static int leftClick = 0;
+ static int rightClick = 0;
+ static Uint32 lastLeftDown = 0;
+ static Uint32 lastRightDown = 0;
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_MOUSEMOTION:
+ {
+ LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordGL openGlCoord;
+ convertCoords(winCoord, &openGlCoord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+ mCallbacks->handleMouseMove(this, openGlCoord, mask);
+ break;
+ }
+
+ case SDL_KEYDOWN:
+ gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod);
+ // part of the fix for SL-13243
+ if (SDLCheckGrabbyKeys(event.key.keysym.sym, TRUE) != 0)
+ SDLReallyCaptureInput(TRUE);
+
+ if (event.key.keysym.unicode)
+ mCallbacks->handleUnicodeChar(event.key.keysym.unicode, gKeyboard->currentMask(FALSE));
+ break;
+
+ case SDL_KEYUP:
+ if (SDLCheckGrabbyKeys(event.key.keysym.sym, FALSE) == 0)
+ SDLReallyCaptureInput(FALSE); // part of the fix for SL-13243
+
+ // This is a testing hack to pop up a dialog when 4 is pressed
+ //if (event.key.keysym.sym == SDLK_4)
+ //OSMessageBox("a whole bunch of text goes right here, whee! test test test.", "this is the title!", OSMB_YESNO);
+
+ gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod);
+ break;
+
+ case SDL_MOUSEBUTTONDOWN:
+ {
+ bool isDoubleClick = false;
+ LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordGL openGlCoord;
+ convertCoords(winCoord, &openGlCoord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ if (event.button.button == SDL_BUTTON_LEFT) // SDL doesn't manage double clicking...
+ {
+ Uint32 now = SDL_GetTicks();
+ if ((now - lastLeftDown) > CLICK_THRESHOLD)
+ leftClick = 1;
+ else
+ {
+ if (++leftClick >= 2)
+ {
+ leftClick = 0;
+ isDoubleClick = true;
+ }
+ }
+ lastLeftDown = now;
+ }
+ else if (event.button.button == SDL_BUTTON_RIGHT)
+ {
+ Uint32 now = SDL_GetTicks();
+ if ((now - lastRightDown) > CLICK_THRESHOLD)
+ rightClick = 1;
+ else
+ {
+ if (++rightClick >= 2)
+ {
+ rightClick = 0;
+ isDoubleClick = true;
+ }
+ }
+ lastRightDown = now;
+ }
+
+ if (event.button.button == SDL_BUTTON_LEFT) // left
+ {
+ if (isDoubleClick)
+ mCallbacks->handleDoubleClick(this, openGlCoord, mask);
+ else
+ mCallbacks->handleMouseDown(this, openGlCoord, mask);
+ }
+
+ else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
+ {
+ // right double click isn't handled right now in Second Life ... if (isDoubleClick)
+ mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
+ }
+
+ else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
+ ; // Middle mouse isn't handled right now in Second Life ... mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
+ else if (event.button.button == 4) // mousewheel up...thanks to X11 for making SDL consider these "buttons".
+ mCallbacks->handleScrollWheel(this, -1);
+ else if (event.button.button == 5) // mousewheel down...thanks to X11 for making SDL consider these "buttons".
+ mCallbacks->handleScrollWheel(this, 1);
+
+ break;
+ }
+
+ case SDL_MOUSEBUTTONUP:
+ {
+ LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordGL openGlCoord;
+ convertCoords(winCoord, &openGlCoord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ if (event.button.button == SDL_BUTTON_LEFT) // left
+ mCallbacks->handleMouseUp(this, openGlCoord, mask);
+ else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
+ mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
+ else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
+ ; // UNUSED IN SECOND LIFE RIGHT NOW mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
+
+ // don't handle mousewheel here...
+
+ break;
+ }
+
+ case SDL_VIDEOEXPOSE: // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing!
+ mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h);
+ break;
+
+ case SDL_VIDEORESIZE: // !!! FIXME: handle this?
+ llinfos << "Handling a resize event: " << event.resize.w <<
+ "x" << event.resize.h << llendl;
+
+ // !!! FIXME: I'm not sure this is necessary!
+ mWindow = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, mSDLFlags);
+ if (!mWindow)
+ {
+ // FIXME: More informative dialog?
+ llinfos << "Could not recreate context after resize! Quitting..." << llendl;
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ break;
+ }
+
+ mCallbacks->handleResize(this, event.resize.w, event.resize.h );
+ break;
+
+ case SDL_ACTIVEEVENT:
+ if (event.active.state & SDL_APPINPUTFOCUS)
+ {
+ // Note that for SDL (particularly on X11), keyboard
+ // and mouse focus are independent things. Here we are
+ // tracking keyboard focus state changes.
+
+ // We have to do our own state massaging because SDL
+ // can send us two unfocus events in a row for example,
+ // which confuses the focus code [SL-24071].
+ if (event.active.gain != mHaveInputFocus)
+ {
+ if (event.active.gain)
+ mCallbacks->handleFocus(this);
+ else
+ mCallbacks->handleFocusLost(this);
+
+ mHaveInputFocus = !!event.active.gain;
+ }
+ }
+ if (event.active.state & SDL_APPACTIVE)
+ {
+ // Change in iconification/minimization state.
+ if ((!event.active.gain) != mIsMinimized)
+ {
+ mCallbacks->handleActivate(this, !!event.active.gain);
+ llinfos << "SDL deiconification state switched to " << BOOL(event.active.gain) << llendl;
+
+ mIsMinimized = (!event.active.gain);
+ }
+ else
+ {
+ llinfos << "Ignored bogus redundant SDL deiconification state switch to " << BOOL(event.active.gain) << llendl;
+ }
+ }
+ break;
+
+ case SDL_QUIT:
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ break;
+ default:
+ //llinfos << "Unhandled SDL event type " << event.type << llendl;
+ break;
+ }
+ }
+
+#if LL_X11
+ // This is a good time to stop flashing the icon if our mFlashTimer has
+ // expired.
+ if (mFlashing && mFlashTimer.hasExpired())
+ {
+ x11_set_urgent(FALSE);
+ mFlashing = FALSE;
+ }
+#endif // LL_X11
+}
+
+static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty)
+{
+ SDL_Cursor *sdlcursor = NULL;
+ SDL_Surface *bmpsurface;
+
+ // Load cursor pixel data from BMP file
+ bmpsurface = Load_BMP_Resource(filename);
+ if (bmpsurface && bmpsurface->w%8==0)
+ {
+ SDL_Surface *cursurface;
+ llinfos << "Loaded cursor file " << filename << " "
+ << bmpsurface->w << "x" << bmpsurface->h << llendl;
+ cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE,
+ bmpsurface->w,
+ bmpsurface->h,
+ 32,
+ 0xFFU,
+ 0xFF00U,
+ 0xFF0000U,
+ 0xFF000000U);
+ SDL_FillRect(cursurface, NULL, 0x00000000U);
+
+ // Blit the cursor pixel data onto a 32-bit RGBA surface so we
+ // only have to cope with processing one type of pixel format.
+ if (0 == SDL_BlitSurface(bmpsurface, NULL,
+ cursurface, NULL))
+ {
+ // n.b. we already checked that width is a multiple of 8.
+ const int bitmap_bytes = (cursurface->w * cursurface->h) / 8;
+ unsigned char *cursor_data = new unsigned char[bitmap_bytes];
+ unsigned char *cursor_mask = new unsigned char[bitmap_bytes];
+ memset(cursor_data, 0, bitmap_bytes);
+ memset(cursor_mask, 0, bitmap_bytes);
+ int i,j;
+ // Walk the RGBA cursor pixel data, extracting both data and
+ // mask to build SDL-friendly cursor bitmaps from. The mask
+ // is inferred by color-keying against 200,200,200
+ for (i=0; i<cursurface->h; ++i) {
+ for (j=0; j<cursurface->w; ++j) {
+ unsigned char *pixelp =
+ ((unsigned char *)cursurface->pixels)
+ + cursurface->pitch * i
+ + j*cursurface->format->BytesPerPixel;
+ unsigned char srcred = pixelp[0];
+ unsigned char srcgreen = pixelp[1];
+ unsigned char srcblue = pixelp[2];
+ BOOL mask_bit = (srcred != 200)
+ || (srcgreen != 200)
+ || (srcblue != 200);
+ BOOL data_bit = mask_bit && (srcgreen <= 80);//not 0x80
+ unsigned char bit_offset = (cursurface->w/8) * i
+ + j/8;
+ cursor_data[bit_offset] |= (data_bit) << (7 - (j&7));
+ cursor_mask[bit_offset] |= (mask_bit) << (7 - (j&7));
+ }
+ }
+ sdlcursor = SDL_CreateCursor((Uint8*)cursor_data,
+ (Uint8*)cursor_mask,
+ cursurface->w, cursurface->h,
+ hotx, hoty);
+ delete[] cursor_data;
+ delete[] cursor_mask;
+ } else {
+ llwarns << "CURSOR BLIT FAILURE, cursurface: " << cursurface << llendl;
+ }
+ SDL_FreeSurface(cursurface);
+ SDL_FreeSurface(bmpsurface);
+ } else {
+ llwarns << "CURSOR LOAD FAILURE " << filename << llendl;
+ }
+
+ return sdlcursor;
+}
+
+void LLWindowSDL::setCursor(ECursorType cursor)
+{
+ if (mCurrentCursor != cursor)
+ {
+ if (cursor < UI_CURSOR_COUNT)
+ {
+ SDL_Cursor *sdlcursor = mSDLCursors[cursor];
+ // Try to default to the arrow for any cursors that
+ // did not load correctly.
+ if (!sdlcursor && mSDLCursors[UI_CURSOR_ARROW])
+ sdlcursor = mSDLCursors[UI_CURSOR_ARROW];
+ if (sdlcursor)
+ SDL_SetCursor(sdlcursor);
+ } else {
+ llwarns << "Tried to set invalid cursor number " << cursor << llendl;
+ }
+ mCurrentCursor = cursor;
+ }
+}
+
+ECursorType LLWindowSDL::getCursor()
+{
+ return mCurrentCursor;
+}
+
+void LLWindowSDL::initCursors()
+{
+ int i;
+ // Blank the cursor pointer array for those we may miss.
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ {
+ mSDLCursors[i] = NULL;
+ }
+ // Pre-make an SDL cursor for each of the known cursor types.
+ // We hardcode the hotspots - to avoid that we'd have to write
+ // a .cur file loader.
+ // NOTE: SDL doesn't load RLE-compressed BMP files.
+ mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0);
+ mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15);
+ mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10);
+ mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16);
+ mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14);
+ mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17);
+ mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17);
+ mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14);
+ mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16);
+ mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
+ mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
+ mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13);
+ mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6);
+ mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5);
+ mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7);
+ mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0);
+ mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8);
+ mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0);
+ mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13);
+ mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLPAY] = makeSDLCursorFromBMP("toolpay.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",0,0);
+ mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
+}
+
+void LLWindowSDL::quitCursors()
+{
+ int i;
+ if (mWindow)
+ {
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ {
+ if (mSDLCursors[i])
+ {
+ SDL_FreeCursor(mSDLCursors[i]);
+ mSDLCursors[i] = NULL;
+ }
+ }
+ } else {
+ // SDL doesn't refcount cursors, so if the window has
+ // already been destroyed then the cursors have gone with it.
+ llinfos << "Skipping quitCursors: mWindow already gone." << llendl;
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ mSDLCursors[i] = NULL;
+ }
+}
+
+void LLWindowSDL::captureMouse()
+{
+ // SDL already enforces the semantics that captureMouse is
+ // used for, i.e. that we continue to get mouse events as long
+ // as a button is down regardless of whether we left the
+ // window, and in a less obnoxious way than SDL_WM_GrabInput
+ // which would confine the cursor to the window too.
+
+ //llinfos << "LLWindowSDL::captureMouse" << llendl;
+}
+
+void LLWindowSDL::releaseMouse()
+{
+ // see LWindowSDL::captureMouse()
+
+ //llinfos << "LLWindowSDL::releaseMouse" << llendl;
+}
+
+void LLWindowSDL::hideCursor()
+{
+ if(!mCursorHidden)
+ {
+ // llinfos << "hideCursor: hiding" << llendl;
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+ SDL_ShowCursor(0);
+ }
+ else
+ {
+ // llinfos << "hideCursor: already hidden" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowSDL::showCursor()
+{
+ if(mCursorHidden)
+ {
+ // llinfos << "showCursor: showing" << llendl;
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+ SDL_ShowCursor(1);
+ }
+ else
+ {
+ // llinfos << "showCursor: already visible" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowSDL::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowSDL::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+
+
+//
+// LLSplashScreenSDL
+//
+LLSplashScreenSDL::LLSplashScreenSDL()
+{
+}
+
+LLSplashScreenSDL::~LLSplashScreenSDL()
+{
+}
+
+void LLSplashScreenSDL::showImpl()
+{
+}
+
+void LLSplashScreenSDL::updateImpl(const char* mesg)
+{
+}
+
+
+void LLSplashScreenSDL::hideImpl()
+{
+}
+
+
+
+#if LL_GTK
+static void response_callback (GtkDialog *dialog,
+ gint arg1,
+ gpointer user_data)
+{
+ gint *response = (gint*)user_data;
+ *response = arg1;
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ gtk_main_quit();
+}
+
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
+{
+ S32 rtn = OSBTN_CANCEL;
+
+#if LL_GTK
+ maybe_do_gtk_diagnostics();
+#endif // LL_GTK
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->beforeDialog();
+
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL)
+ // We can NOT expect to combine GTK and SDL's aggressive fullscreen
+ && ((NULL==gWindowImplementation) || (!was_fullscreen))
+ )
+ {
+ GtkWidget *win = NULL;
+
+ llinfos << "Creating a dialog because we're in windowed mode and GTK is happy." << llendl;
+
+ GtkDialogFlags flags = GTK_DIALOG_MODAL;
+ GtkMessageType messagetype;
+ GtkButtonsType buttons;
+ switch (type)
+ {
+ default:
+ case OSMB_OK:
+ messagetype = GTK_MESSAGE_WARNING;
+ buttons = GTK_BUTTONS_OK;
+ break;
+ case OSMB_OKCANCEL:
+ messagetype = GTK_MESSAGE_QUESTION;
+ buttons = GTK_BUTTONS_OK_CANCEL;
+ break;
+ case OSMB_YESNO:
+ messagetype = GTK_MESSAGE_QUESTION;
+ buttons = GTK_BUTTONS_YES_NO;
+ break;
+ }
+ win = gtk_message_dialog_new(NULL,
+ flags, messagetype, buttons,
+ text);
+
+# if LL_X11
+ // Make GTK tell the window manager to associate this
+ // dialog with our non-GTK SDL window, which should try
+ // to keep it on top etc.
+ if (SDL_XWindowID != None)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+# endif //LL_X11
+
+ gtk_window_set_position(GTK_WINDOW(win),
+ GTK_WIN_POS_CENTER_ON_PARENT);
+
+ gtk_window_set_type_hint(GTK_WINDOW(win),
+ GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ if (caption)
+ gtk_window_set_title(GTK_WINDOW(win), caption);
+
+ gint response = GTK_RESPONSE_NONE;
+ g_signal_connect (win,
+ "response",
+ G_CALLBACK (response_callback),
+ &response);
+
+ // we should be able to us a gtk_dialog_run(), but it's
+ // apparently not written to exist in a world without a higher
+ // gtk_main(), so we manage its signal/destruction outselves.
+ gtk_widget_show_all (win);
+ gtk_main();
+
+ //llinfos << "response: " << response << llendl;
+ switch (response)
+ {
+ case GTK_RESPONSE_OK: rtn = OSBTN_OK; break;
+ case GTK_RESPONSE_YES: rtn = OSBTN_YES; break;
+ case GTK_RESPONSE_NO: rtn = OSBTN_NO; break;
+ case GTK_RESPONSE_APPLY: rtn = OSBTN_OK; break;
+ case GTK_RESPONSE_NONE:
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_CLOSE:
+ case GTK_RESPONSE_DELETE_EVENT:
+ default: rtn = OSBTN_CANCEL;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
+ llinfos << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << llendl;
+ rtn = OSBTN_OK;
+ }
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->afterDialog();
+
+ return rtn;
+}
+
+static void color_changed_callback(GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION(widget);
+ GdkColor *colorp = (GdkColor*)user_data;
+
+ gtk_color_selection_get_current_color(colorsel, colorp);
+}
+
+BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ BOOL rtn = FALSE;
+
+ beforeDialog();
+
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL)
+ // We can NOT expect to combine GTK and SDL's aggressive fullscreen
+ && !was_fullscreen
+ )
+ {
+ GtkWidget *win = NULL;
+
+ win = gtk_color_selection_dialog_new(NULL);
+
+# if LL_X11
+ // Get GTK to tell the window manager to associate this
+ // dialog with our non-GTK SDL window, which should try
+ // to keep it on top etc.
+ if (SDL_XWindowID != None)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+# endif //LL_X11
+
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel);
+
+ GdkColor color, orig_color;
+ orig_color.red = guint16(65535 * *r);
+ orig_color.green= guint16(65535 * *g);
+ orig_color.blue = guint16(65535 * *b);
+ color = orig_color;
+
+ gtk_color_selection_set_previous_color (colorsel, &color);
+ gtk_color_selection_set_current_color (colorsel, &color);
+ gtk_color_selection_set_has_palette (colorsel, TRUE);
+ gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
+
+ gint response = GTK_RESPONSE_NONE;
+ g_signal_connect (win,
+ "response",
+ G_CALLBACK (response_callback),
+ &response);
+
+ g_signal_connect (G_OBJECT (colorsel), "color_changed",
+ G_CALLBACK (color_changed_callback),
+ &color);
+
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+ gtk_widget_show_all(win);
+ // hide the help button - we don't service it.
+ gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button);
+ gtk_main();
+
+ if (response == GTK_RESPONSE_OK &&
+ (orig_color.red != color.red
+ || orig_color.green != color.green
+ || orig_color.blue != color.blue) )
+ {
+ *r = color.red / 65535.0f;
+ *g = color.green / 65535.0f;
+ *b = color.blue / 65535.0f;
+ rtn = TRUE;
+ }
+ }
+
+ afterDialog();
+
+ return rtn;
+}
+#else
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
+{
+ fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
+ return 0;
+}
+
+BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ return (FALSE);
+}
+#endif // LL_GTK
+
+// Open a URL with the user's default web browser.
+// Must begin with protocol identifier.
+void spawn_web_browser(const char* escaped_url)
+{
+ llinfos << "spawn_web_browser: " << escaped_url << llendl;
+
+#if LL_LINUX
+# if LL_X11
+ if (SDL_Display) // Just in case - before forking.
+ XSync(SDL_Display, False);
+# endif // LL_X11
+
+ std::string cmd;
+ cmd = gDirUtilp->getAppRODataDir().c_str();
+ cmd += gDirUtilp->getDirDelimiter().c_str();
+ cmd += "launch_url.sh";
+ char* const argv[] = {(char*)cmd.c_str(), (char*)escaped_url, NULL};
+
+ pid_t pid = fork();
+ if (pid == 0)
+ { // child
+ // disconnect from stdin/stdout/stderr, or child will
+ // keep our output pipe undesirably alive if it outlives us.
+ close(0);
+ close(1);
+ close(2);
+ // end ourself by running the command
+ execv(cmd.c_str(), argv);
+ // if execv returns at all, there was a problem.
+ llwarns << "execv failure when trying to start " << cmd << llendl;
+ _exit(1); // _exit because we don't want atexit() clean-up!
+ } else {
+ if (pid > 0)
+ {
+ // parent - wait for child to die
+ int childExitStatus;
+ waitpid(pid, &childExitStatus, 0);
+ } else {
+ llwarns << "fork failure." << llendl;
+ }
+ }
+#endif // LL_LINUX
+
+ llinfos << "spawn_web_browser returning." << llendl;
+}
+
+void shell_open( const char* file_path )
+{
+ // !!! FIXME:
+ fprintf(stderr, "shell_open: %s\n", file_path);
+}
+
+void *LLWindowSDL::getPlatformWindow()
+{
+#if LL_X11
+ // pointer to our static raw X window
+ return (void*)&SDL_XWindowID;
+#else
+ // doubt we really want to return a high-level SDL structure here.
+ return NULL;
+#endif
+}
+
+void LLWindowSDL::bringToFront()
+{
+ // !!! FIXME:
+ fprintf(stderr, "bringToFront\n");
+}
+
+#endif // LL_SDL