llwindowsdl.cpp

Go to the documentation of this file.
00001 
00032 #if LL_SDL
00033 
00034 #include "linden_common.h"
00035 
00036 #include "llwindowsdl.h"
00037 #include "llkeyboardsdl.h"
00038 #include "llerror.h"
00039 #include "llgl.h"
00040 #include "llstring.h"
00041 #include "lldir.h"
00042 
00043 #include "llglheaders.h"
00044 
00045 #include "indra_constants.h"
00046 
00047 #if LL_GTK
00048 extern "C" {
00049 # include "gtk/gtk.h"
00050 }
00051 #endif // LL_GTK
00052 
00053 #if LL_LINUX || LL_SOLARIS
00054 // not necessarily available on random SDL platforms, so #if LL_LINUX
00055 // for execv(), waitpid(), fork()
00056 # include <unistd.h>
00057 # include <sys/types.h>
00058 # include <sys/wait.h>
00059 #endif // LL_LINUX || LL_SOLARIS
00060 
00061 extern BOOL gDebugWindowProc;
00062 
00063 const S32       MAX_NUM_RESOLUTIONS = 32;
00064 
00065 //
00066 // LLWindowSDL
00067 //
00068 
00069 #if LL_X11
00070 # include <X11/Xutil.h>
00071 #endif //LL_X11
00072 
00073 // TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for a similar
00074 // set of reasons): Stash a pointer to the LLWindowSDL object here and
00075 // maintain in the constructor and destructor.  This assumes that there will
00076 // be only one object of this class at any time.  Currently this is true.
00077 static LLWindowSDL *gWindowImplementation = NULL;
00078 
00079 static BOOL was_fullscreen = FALSE;
00080 
00081 
00082 void maybe_lock_display(void)
00083 {
00084         if (gWindowImplementation) {
00085                 gWindowImplementation->Lock_Display();
00086         }
00087 }
00088 
00089 
00090 void maybe_unlock_display(void)
00091 {
00092         if (gWindowImplementation) {
00093                 gWindowImplementation->Unlock_Display();
00094         }
00095 }
00096 
00097 
00098 #if LL_GTK
00099 // Lazily initialize and check the runtime GTK version for goodness.
00100 BOOL ll_try_gtk_init(void)
00101 {
00102         static BOOL done_gtk_diag = FALSE;
00103         static BOOL gtk_is_good = FALSE;
00104         static BOOL done_setlocale = FALSE;
00105         static BOOL tried_gtk_init = FALSE;
00106 
00107         if (!done_setlocale)
00108         {
00109                 llinfos << "Starting GTK Initialization." << llendl;
00110                 maybe_lock_display();
00111                 gtk_disable_setlocale();
00112                 maybe_unlock_display();
00113                 done_setlocale = TRUE;
00114         }
00115         
00116         if (!tried_gtk_init)
00117         {
00118                 tried_gtk_init = TRUE;
00119                 if (!g_thread_supported ()) g_thread_init (NULL);
00120                 maybe_lock_display();
00121                 gtk_is_good = gtk_init_check(NULL, NULL);
00122                 maybe_unlock_display();
00123                 if (!gtk_is_good)
00124                         llwarns << "GTK Initialization failed." << llendl;
00125         }
00126 
00127         if (gtk_is_good && !done_gtk_diag)
00128         {
00129                 llinfos << "GTK Initialized." << llendl;
00130                 llinfos << "- Compiled against GTK version "
00131                         << GTK_MAJOR_VERSION << "."
00132                         << GTK_MINOR_VERSION << "."
00133                         << GTK_MICRO_VERSION << llendl;
00134                 llinfos << "- Running against GTK version "
00135                         << gtk_major_version << "."
00136                         << gtk_minor_version << "."
00137                         << gtk_micro_version << llendl;
00138                 gchar *gtk_warning;
00139                 maybe_lock_display();
00140                 gtk_warning = gtk_check_version(GTK_MAJOR_VERSION,
00141                                                 GTK_MINOR_VERSION,
00142                                                 GTK_MICRO_VERSION);
00143                 maybe_unlock_display();
00144                 if (gtk_warning)
00145                 {
00146                         llwarns << "- GTK COMPATIBILITY WARNING: " <<
00147                                 gtk_warning << llendl;
00148                         gtk_is_good = FALSE;
00149                 }
00150 
00151                 done_gtk_diag = TRUE;
00152         }
00153 
00154         return gtk_is_good;
00155 }
00156 #endif // LL_GTK
00157 
00158 
00159 #if LL_X11
00160 Window get_SDL_XWindowID(void)
00161 {
00162         if (gWindowImplementation) {
00163                 return gWindowImplementation->mSDL_XWindowID;
00164         }
00165         return None;
00166 }
00167 
00168 Display* get_SDL_Display(void)
00169 {
00170         if (gWindowImplementation) {
00171                 return gWindowImplementation->mSDL_Display;
00172         }
00173         return NULL;
00174 }
00175 #endif // LL_X11
00176 
00177 
00178 BOOL check_for_card(const char* RENDERER, const char* bad_card)
00179 {
00180         if (!strncasecmp(RENDERER, bad_card, strlen(bad_card)))
00181         {
00182                 char buffer[1024];      /* Flawfinder: ignore */
00183                 snprintf(buffer, sizeof(buffer),        
00184                         "Your video card appears to be a %s, which Second Life does not support.\n"
00185                         "\n"
00186                         "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
00187                         "multitexture support.  We explicitly support nVidia GeForce 2 or better, \n"
00188                         "and ATI Radeon 8500 or better.\n"
00189                         "\n"
00190                         "If you own a supported card and continue to receive this message, try \n"
00191                         "updating to the latest video card drivers. Otherwise look in the\n"
00192                         "secondlife.com support section or e-mail technical support\n"
00193                         "\n"
00194                         "You can try to run Second Life, but it will probably crash or run\n"
00195                         "very slowly.  Try anyway?",
00196                         bad_card);
00197                 S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
00198                 if (OSBTN_YES == button)
00199                 {
00200                         return FALSE;
00201                 }
00202                 else
00203                 {
00204                         return TRUE;
00205                 }
00206         }
00207 
00208         return FALSE;
00209 }
00210 
00211 
00212 
00213 
00214 LLWindowSDL::LLWindowSDL(char *title, S32 x, S32 y, S32 width,
00215                                                            S32 height, U32 flags,
00216                                                            BOOL fullscreen, BOOL clearBg,
00217                                                            BOOL disable_vsync, BOOL use_gl,
00218                                                            BOOL ignore_pixel_depth,
00219                                                            S32 stereo_mode)
00220         : LLWindow(fullscreen, flags), mGamma(1.0f)
00221 {
00222         // Initialize the keyboard
00223         gKeyboard = new LLKeyboardSDL();
00224         // Note that we can't set up key-repeat until after SDL has init'd video
00225 
00226         // Ignore use_gl for now, only used for drones on PC
00227         mWindow = NULL;
00228         mCursorDecoupled = FALSE;
00229         mCursorLastEventDeltaX = 0;
00230         mCursorLastEventDeltaY = 0;
00231         mCursorIgnoreNextDelta = FALSE;
00232         mNeedsResize = FALSE;
00233         mOverrideAspectRatio = 0.f;
00234         mGrabbyKeyFlags = 0;
00235         mReallyCapturedCount = 0;
00236         mHaveInputFocus = -1;
00237         mIsMinimized = -1;
00238 
00239 #if LL_X11
00240         mSDL_XWindowID = None;
00241         mSDL_Display = NULL;
00242 #endif // LL_X11
00243 
00244 #if LL_GTK
00245         // We MUST be the first to initialize GTK, i.e. we have to beat
00246         // our embedded Mozilla to the punch so that GTK doesn't get badly
00247         // initialized with a non-C locale and cause lots of serious random
00248         // weirdness.
00249         ll_try_gtk_init();
00250 #endif // LL_GTK
00251 
00252         // Get the original aspect ratio of the main device.
00253         mOriginalAspectRatio = 1024.0 / 768.0;  // !!! *FIX: ? //(double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
00254 
00255         if (!title)
00256                 title = "SDL Window";  // *FIX: (???)
00257 
00258         // Stash the window title
00259         mWindowTitle = new char[strlen(title) + 1]; /* Flawfinder: ignore */
00260         if(mWindowTitle == NULL)
00261         {
00262                 llerrs << "Memory allocation failure" << llendl;
00263                 return;
00264         }
00265 
00266         strcpy(mWindowTitle, title); /* Flawfinder: ignore */
00267         // Create the GL context and set it up for windowed or fullscreen, as appropriate.
00268         if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
00269         {
00270                 gGLManager.initGL();
00271 
00272                 //start with arrow cursor
00273                 initCursors();
00274                 setCursor( UI_CURSOR_ARROW );
00275         }
00276 
00277         stop_glerror();
00278 
00279         // Stash an object pointer for OSMessageBox()
00280         gWindowImplementation = this;
00281 
00282 #if LL_X11
00283         mFlashing = FALSE;
00284 #endif // LL_X11
00285 }
00286 
00287 static SDL_Surface *Load_BMP_Resource(const char *basename)
00288 {
00289         const int PATH_BUFFER_SIZE=1000;
00290         char path_buffer[PATH_BUFFER_SIZE];     /* Flawfinder: ignore */
00291         
00292         // Figure out where our BMP is living on the disk
00293         snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",    
00294                  gDirUtilp->getAppRODataDir().c_str(),
00295                  gDirUtilp->getDirDelimiter().c_str(),
00296                  gDirUtilp->getDirDelimiter().c_str(),
00297                  basename);
00298         path_buffer[PATH_BUFFER_SIZE-1] = '\0';
00299         
00300         return SDL_LoadBMP(path_buffer);
00301 }
00302 
00303 #if LL_X11
00304 // This is an XFree86/XOrg-specific hack for detecting the amount of Video RAM
00305 // on this machine.  It works by searching /var/log/var/log/Xorg.?.log or
00306 // /var/log/XFree86.?.log for a ': (VideoRAM|Memory): (%d+) kB' regex, where
00307 // '?' is the X11 display number derived from $DISPLAY
00308 static int x11_detect_VRAM_kb_fp(FILE *fp, const char *prefix_str)
00309 {
00310         const int line_buf_size = 1000;
00311         char line_buf[line_buf_size];
00312         while (fgets(line_buf, line_buf_size, fp))
00313         {
00314                 //lldebugs << "XLOG: " << line_buf << llendl;
00315 
00316                 // Why the ad-hoc parser instead of using a regex?  Our
00317                 // favourite regex implementation - libboost_regex - is
00318                 // quite a heavy and troublesome dependency for the client, so
00319                 // it seems a shame to introduce it for such a simple task.
00320                 const char *part1_template = prefix_str;
00321                 const char part2_template[] = " kB";
00322                 char *part1 = strstr(line_buf, part1_template);
00323                 if (part1) // found start of matching line
00324                 {
00325                         part1 = &part1[strlen(part1_template)]; // -> after
00326                         char *part2 = strstr(part1, part2_template);
00327                         if (part2) // found end of matching line
00328                         {
00329                                 // now everything between part1 and part2 is
00330                                 // supposed to be numeric, describing the
00331                                 // number of kB of Video RAM supported
00332                                 int rtn = 0;
00333                                 for (; part1 < part2; ++part1)
00334                                 {
00335                                         if (*part1 < '0' || *part1 > '9')
00336                                         {
00337                                                 // unexpected char, abort parse
00338                                                 rtn = 0;
00339                                                 break;
00340                                         }
00341                                         rtn *= 10;
00342                                         rtn += (*part1) - '0';
00343                                 }
00344                                 if (rtn > 0)
00345                                 {
00346                                         // got the kB number.  return it now.
00347                                         return rtn;
00348                                 }
00349                         }
00350                 }
00351         }
00352         return 0; // 'could not detect'
00353 }
00354 
00355 static int x11_detect_VRAM_kb()
00356 {
00357 #if LL_SOLARIS
00358 #error Can this be done without an explicit architecture test, ie a test FOR xorg? Was followed by: && defined(__sparc)
00359       //  NOTE: there's no Xorg server on SPARC so just return 0
00360       //        and allow SDL to attempt to get the amount of VRAM
00361       return(0);
00362 #else
00363 
00364         std::string x_log_location("/var/log/");
00365         std::string fname;
00366         int rtn = 0; // 'could not detect'
00367         int display_num = 0;
00368         FILE *fp;
00369         char *display_env = getenv("DISPLAY"); // e.g. :0 or :0.0 or :1.0 etc
00370         // parse DISPLAY number so we can go grab the right log file
00371         if (display_env[0] == ':' &&
00372             display_env[1] >= '0' && display_env[1] <= '9')
00373         {
00374                 display_num = display_env[1] - '0';
00375         }
00376 
00377         // *TODO: we could be smarter and see which of Xorg/XFree86 has the
00378         // freshest time-stamp.
00379 
00380         // Try Xorg log first
00381         fname = x_log_location;
00382         fname += "Xorg.";
00383         fname += ('0' + display_num);
00384         fname += ".log";
00385         fp = fopen(fname.c_str(), "r");
00386         if (fp)
00387         {
00388                 llinfos << "Looking in " << fname
00389                         << " for VRAM info..." << llendl;
00390                 rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
00391                 fclose(fp);
00392                 if (0 == rtn)
00393                 {
00394                         fp = fopen(fname.c_str(), "r");
00395                         if (fp)
00396                         {
00397                                 rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
00398                                 fclose(fp);
00399                         }
00400                 }
00401         }
00402         else
00403         {
00404                 llinfos << "Could not open " << fname
00405                         << " - skipped." << llendl;     
00406                 // Try old XFree86 log otherwise
00407                 fname = x_log_location;
00408                 fname += "XFree86.";
00409                 fname += ('0' + display_num);
00410                 fname += ".log";
00411                 fp = fopen(fname.c_str(), "r");
00412                 if (fp)
00413                 {
00414                         llinfos << "Looking in " << fname
00415                                 << " for VRAM info..." << llendl;
00416                         rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
00417                         fclose(fp);
00418                         if (0 == rtn)
00419                         {
00420                                 fp = fopen(fname.c_str(), "r");
00421                                 if (fp)
00422                                 {
00423                                         rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
00424                                         fclose(fp);
00425                                 }
00426                         }
00427                 }
00428                 else
00429                 {
00430                         llinfos << "Could not open " << fname
00431                                 << " - skipped." << llendl;
00432                 }
00433         }
00434         return rtn;
00435 #endif // LL_SOLARIS
00436 }
00437 #endif // LL_X11
00438 
00439 BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
00440 {
00441         //bool                  glneedsinit = false;
00442 //    const char *gllibname = null;
00443 
00444         llinfos << "createContext, fullscreen=" << fullscreen <<
00445             " size=" << width << "x" << height << llendl;
00446 
00447         // captures don't survive contexts
00448         mGrabbyKeyFlags = 0;
00449         mReallyCapturedCount = 0;
00450         
00451         if (SDL_Init(SDL_INIT_VIDEO) < 0)
00452         {
00453                 llinfos << "sdl_init() failed! " << SDL_GetError() << llendl;
00454                 setupFailure("window creation error", "error", OSMB_OK);
00455                 return false;
00456         }
00457 
00458         SDL_version c_sdl_version;
00459         SDL_VERSION(&c_sdl_version);
00460         llinfos << "Compiled against SDL "
00461                 << int(c_sdl_version.major) << "."
00462                 << int(c_sdl_version.minor) << "."
00463                 << int(c_sdl_version.patch) << llendl;
00464         const SDL_version *r_sdl_version;
00465         r_sdl_version = SDL_Linked_Version();
00466         llinfos << " Running against SDL "
00467                 << int(r_sdl_version->major) << "."
00468                 << int(r_sdl_version->minor) << "."
00469                 << int(r_sdl_version->patch) << llendl;
00470 
00471         const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo( );
00472         if (!videoInfo)
00473         {
00474                 llinfos << "SDL_GetVideoInfo() failed! " << SDL_GetError() << llendl;
00475                 setupFailure("Window creation error", "Error", OSMB_OK);
00476                 return FALSE;
00477         }
00478 
00479         SDL_EnableUNICODE(1);
00480         SDL_WM_SetCaption(mWindowTitle, mWindowTitle);
00481 
00482         // Set the application icon.
00483         SDL_Surface *bmpsurface;
00484         bmpsurface = Load_BMP_Resource("ll_icon.BMP");
00485         if (bmpsurface)
00486         {
00487                 // This attempts to give a black-keyed mask to the icon.
00488                 SDL_SetColorKey(bmpsurface,
00489                                 SDL_SRCCOLORKEY,
00490                                 SDL_MapRGB(bmpsurface->format, 0,0,0) );
00491                 SDL_WM_SetIcon(bmpsurface, NULL);
00492                 // The SDL examples cheerfully avoid freeing the icon
00493                 // surface, but I'm betting that's leaky.
00494                 SDL_FreeSurface(bmpsurface);
00495                 bmpsurface = NULL;
00496         }
00497 
00498         // note: these SetAttributes make Tom's 9600-on-AMD64 fail to
00499         // get a visual, but it's broken anyway when it does, and without
00500         // these SetAttributes we might easily get an avoidable substandard
00501         // visual to work with on most other machines.
00502         SDL_GL_SetAttribute(SDL_GL_RED_SIZE,  8);
00503         SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
00504         SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
00505 #if !LL_SOLARIS
00506         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24);
00507 #else
00508         // NOTE- use smaller Z-buffer to enable more graphics cards
00509         //     - This should not affect better GPUs and has been proven
00510         //       to provide 24-bit z-buffers when available.
00511         //
00512         // As the API states: 
00513         //
00514         // GLX_DEPTH_SIZE    Must be followed by a nonnegative
00515         //                   minimum size specification.  If this
00516         //                   value is zero, visuals with no depth
00517         //                   buffer are preferred.  Otherwise, the
00518         //                   largest available depth buffer of at
00519         //                   least the minimum size is preferred.
00520 
00521         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
00522 #endif
00523         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
00524 
00525         // *FIX: try to toggle vsync here?
00526 
00527         mFullscreen = fullscreen;
00528         was_fullscreen = fullscreen;
00529 
00530         int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
00531 
00532         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
00533 
00534         mSDLFlags = sdlflags;
00535 
00536         if (mFullscreen)
00537         {
00538                 llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
00539 
00540                 // If the requested width or height is 0, find the best default for the monitor.
00541                 if((width == 0) || (height == 0))
00542                 {
00543                         // Scan through the list of modes, looking for one which has:
00544                         //              height between 700 and 800
00545                         //              aspect ratio closest to the user's original mode
00546                         S32 resolutionCount = 0;
00547                         LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
00548 
00549                         if(resolutionList != NULL)
00550                         {
00551                                 F32 closestAspect = 0;
00552                                 U32 closestHeight = 0;
00553                                 U32 closestWidth = 0;
00554                                 int i;
00555 
00556                                 llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
00557 
00558                                 for(i=0; i < resolutionCount; i++)
00559                                 {
00560                                         F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
00561 
00562                                         llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
00563 
00564                                         if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
00565                                                 (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
00566                                         {
00567                                                 llinfos << " (new closest mode) " << llendl;
00568 
00569                                                 // This is the closest mode we've seen yet.
00570                                                 closestWidth = resolutionList[i].mWidth;
00571                                                 closestHeight = resolutionList[i].mHeight;
00572                                                 closestAspect = aspect;
00573                                         }
00574                                 }
00575 
00576                                 width = closestWidth;
00577                                 height = closestHeight;
00578                         }
00579                 }
00580 
00581                 if((width == 0) || (height == 0))
00582                 {
00583                         // Mode search failed for some reason.  Use the old-school default.
00584                         width = 1024;
00585                         height = 768;
00586                 }
00587 
00588                 mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
00589 
00590                 if (mWindow)
00591                 {
00592                         mFullscreen = TRUE;
00593                         was_fullscreen = TRUE;
00594                         mFullscreenWidth   = mWindow->w;
00595                         mFullscreenHeight  = mWindow->h;
00596                         mFullscreenBits    = mWindow->format->BitsPerPixel;
00597                         mFullscreenRefresh = -1;
00598 
00599                         llinfos << "Running at " << mFullscreenWidth
00600                                 << "x"   << mFullscreenHeight
00601                                 << "x"   << mFullscreenBits
00602                                 << " @ " << mFullscreenRefresh
00603                                 << llendl;
00604                 }
00605                 else
00606                 {
00607                         llwarns << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << llendl;
00608                         // No fullscreen support
00609                         mFullscreen = FALSE;
00610                         was_fullscreen = FALSE;
00611                         mFullscreenWidth   = -1;
00612                         mFullscreenHeight  = -1;
00613                         mFullscreenBits    = -1;
00614                         mFullscreenRefresh = -1;
00615 
00616                         char error[256];        /* Flawfinder: ignore */
00617                         snprintf(error, sizeof(error), "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);      
00618                         OSMessageBox(error, "Error", OSMB_OK);
00619                 }
00620         }
00621 
00622         if(!mFullscreen && (mWindow == NULL))
00623         {
00624                 if (width == 0)
00625                     width = 1024;
00626                 if (height == 0)
00627                     width = 768;
00628 
00629                 llinfos << "createContext: creating window " << width << "x" << height << "x" << bits << llendl;
00630                 mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
00631 
00632                 if (!mWindow)
00633                 {
00634                         llwarns << "createContext: window creation failure. SDL: " << SDL_GetError() << llendl;
00635                         setupFailure("Window creation error", "Error", OSMB_OK);
00636                         return FALSE;
00637                 }
00638         } else if (!mFullscreen && (mWindow != NULL))
00639         {
00640                 llinfos << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << llendl;
00641         }
00642         
00643         // Detect video memory size.
00644 # if LL_X11
00645         gGLManager.mVRAM = x11_detect_VRAM_kb() / 1024;
00646         if (gGLManager.mVRAM != 0)
00647         {
00648                 llinfos << "X11 log-parser detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
00649         } else
00650 # endif // LL_X11
00651         {
00652                 // fallback to letting SDL detect VRAM.
00653                 // note: I've not seen SDL's detection ever actually find
00654                 // VRAM != 0, but if SDL *does* detect it then that's a bonus.
00655                 gGLManager.mVRAM = videoInfo->video_mem / 1024;
00656                 if (gGLManager.mVRAM != 0)
00657                 {
00658                         llinfos << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
00659                 }
00660         }
00661         // If VRAM is not detected, that is handled later
00662 
00663         // *TODO: Now would be an appropriate time to check for some
00664         // explicitly unsupported cards.
00665         //const char* RENDERER = (const char*) glGetString(GL_RENDERER);
00666 
00667         GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits;
00668 
00669         glGetIntegerv(GL_RED_BITS, &redBits);
00670         glGetIntegerv(GL_GREEN_BITS, &greenBits);
00671         glGetIntegerv(GL_BLUE_BITS, &blueBits);
00672         glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
00673         glGetIntegerv(GL_DEPTH_BITS, &depthBits);
00674         glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
00675         
00676         llinfos << "GL buffer:" << llendl
00677         llinfos << "  Red Bits " << S32(redBits) << llendl
00678         llinfos << "  Green Bits " << S32(greenBits) << llendl
00679         llinfos << "  Blue Bits " << S32(blueBits) << llendl
00680         llinfos << "  Alpha Bits " << S32(alphaBits) << llendl
00681         llinfos << "  Depth Bits " << S32(depthBits) << llendl
00682         llinfos << "  Stencil Bits " << S32(stencilBits) << llendl;
00683 
00684         GLint colorBits = redBits + greenBits + blueBits + alphaBits;
00685         // fixme: actually, it's REALLY important for picking that we get at
00686         // least 8 bits each of red,green,blue.  Alpha we can be a bit more
00687         // relaxed about if we have to.
00688 #if LL_SOLARIS
00689 #error && defined(__sparc)
00690         if(colorBits < 24)              //HACK:  on SPARC allow 24-bit color
00691 #else
00692         if (colorBits < 32)
00693 #endif
00694         {
00695                 close();
00696                 setupFailure(
00697 #if LL_SOLARIS
00698 #error && defined(__sparc)
00699                         "Second Life requires at least 24-bit color on SPARC to run in a window.\n"
00700                         "Please use fbconfig to set your default color depth to 24 bits.\n"
00701                         "You may also need to adjust the X11 setting in SMF.  To do so use\n"
00702                         "  'svccfg -s svc:/application/x11/x11-server setprop options/default_depth=24'\n"
00703 #else
00704                         "Second Life requires True Color (32-bit) to run in a window.\n"
00705                         "Please go to Control Panels -> Display -> Settings and\n"
00706                         "set the screen to 32-bit color.\n"
00707 #endif
00708                         "Alternately, if you choose to run fullscreen, Second Life\n"
00709                         "will automatically adjust the screen each time it runs.",
00710                         "Error",
00711                         OSMB_OK);
00712                 return FALSE;
00713         }
00714 
00715 #if 0  // *FIX: we're going to brave it for now...
00716         if (alphaBits < 8)
00717         {
00718                 close();
00719                 setupFailure(
00720                         "Second Life is unable to run because it can't get an 8 bit alpha\n"
00721                         "channel.  Usually this is due to video card driver issues.\n"
00722                         "Please make sure you have the latest video card drivers installed.\n"
00723                         "Also be sure your monitor is set to True Color (32-bit) in\n"
00724                         "Control Panels -> Display -> Settings.\n"
00725                         "If you continue to receive this message, contact customer service.",
00726                         "Error",
00727                         OSMB_OK);
00728                 return FALSE;
00729         }
00730 #endif
00731 
00732 #if LL_X11
00733         init_x11clipboard();
00734 #endif // LL_X11
00735         
00736         // We need to do this here, once video is init'd
00737         if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
00738                                       SDL_DEFAULT_REPEAT_INTERVAL))
00739             llwarns << "Couldn't enable key-repeat: " << SDL_GetError() <<llendl;
00740 
00741         // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
00742         return TRUE;
00743 }
00744 
00745 
00746 // changing fullscreen resolution, or switching between windowed and fullscreen mode.
00747 BOOL LLWindowSDL::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
00748 {
00749         const BOOL needsRebuild = TRUE;  // Just nuke the context and start over.
00750         BOOL result = true;
00751 
00752         llinfos << "switchContext, fullscreen=" << fullscreen << llendl;
00753         stop_glerror();
00754         if(needsRebuild)
00755         {
00756                 destroyContext();
00757                 result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
00758                 if (result)
00759                 {
00760                         gGLManager.initGL();
00761 
00762                         //start with arrow cursor
00763                         initCursors();
00764                         setCursor( UI_CURSOR_ARROW );
00765                 }
00766         }
00767 
00768         stop_glerror();
00769 
00770         return result;
00771 }
00772 
00773 void LLWindowSDL::destroyContext()
00774 {
00775         llinfos << "destroyContext begins" << llendl;
00776 #if LL_X11
00777         quit_x11clipboard();
00778 #endif // LL_X11
00779 
00780         // Clean up remaining GL state before blowing away window
00781         llinfos << "shutdownGL begins" << llendl;
00782         gGLManager.shutdownGL();
00783         llinfos << "SDL_QuitSS/VID begins" << llendl;
00784         SDL_QuitSubSystem(SDL_INIT_VIDEO);  // *FIX: this might be risky...
00785         //unload_all_glsyms();
00786 
00787         mWindow = NULL;
00788 }
00789 
00790 LLWindowSDL::~LLWindowSDL()
00791 {
00792         quitCursors();
00793         destroyContext();
00794 
00795         if(mSupportedResolutions != NULL)
00796         {
00797                 delete []mSupportedResolutions;
00798         }
00799 
00800         delete[] mWindowTitle;
00801 
00802         gWindowImplementation = NULL;
00803 }
00804 
00805 
00806 void LLWindowSDL::show()
00807 {
00808     // *FIX: What to do with SDL?
00809 }
00810 
00811 void LLWindowSDL::hide()
00812 {
00813     // *FIX: What to do with SDL?
00814 }
00815 
00816 void LLWindowSDL::minimize()
00817 {
00818     // *FIX: What to do with SDL?
00819 }
00820 
00821 void LLWindowSDL::restore()
00822 {
00823     // *FIX: What to do with SDL?
00824 }
00825 
00826 
00827 // close() destroys all OS-specific code associated with a window.
00828 // Usually called from LLWindowManager::destroyWindow()
00829 void LLWindowSDL::close()
00830 {
00831         // Is window is already closed?
00832         //      if (!mWindow)
00833         //      {
00834         //              return;
00835         //      }
00836 
00837         // Make sure cursor is visible and we haven't mangled the clipping state.
00838         setMouseClipping(FALSE);
00839         showCursor();
00840 
00841         destroyContext();
00842 }
00843 
00844 BOOL LLWindowSDL::isValid()
00845 {
00846         return (mWindow != NULL);
00847 }
00848 
00849 BOOL LLWindowSDL::getVisible()
00850 {
00851         BOOL result = FALSE;
00852 
00853     // *FIX: This isn't really right...
00854         // Then what is?
00855         if (mWindow)
00856         {
00857                 result = TRUE;
00858         }
00859 
00860         return(result);
00861 }
00862 
00863 BOOL LLWindowSDL::getMinimized()
00864 {
00865         BOOL result = FALSE;
00866 
00867         if (mWindow && (1 == mIsMinimized))
00868         {
00869                 result = TRUE;
00870         }
00871         return(result);
00872 }
00873 
00874 BOOL LLWindowSDL::getMaximized()
00875 {
00876         BOOL result = FALSE;
00877 
00878         if (mWindow)
00879         {
00880                 // TODO
00881         }
00882 
00883         return(result);
00884 }
00885 
00886 BOOL LLWindowSDL::maximize()
00887 {
00888         // TODO
00889         return FALSE;
00890 }
00891 
00892 BOOL LLWindowSDL::getFullscreen()
00893 {
00894         return mFullscreen;
00895 }
00896 
00897 BOOL LLWindowSDL::getPosition(LLCoordScreen *position)
00898 {
00899     // *FIX: can anything be done with this?
00900         position->mX = 0;
00901         position->mY = 0;
00902     return TRUE;
00903 }
00904 
00905 BOOL LLWindowSDL::getSize(LLCoordScreen *size)
00906 {
00907     if (mWindow)
00908     {
00909         size->mX = mWindow->w;
00910         size->mY = mWindow->h;
00911                 return (TRUE);
00912     }
00913 
00914         llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
00915     return (FALSE);
00916 }
00917 
00918 BOOL LLWindowSDL::getSize(LLCoordWindow *size)
00919 {
00920     if (mWindow)
00921     {
00922         size->mX = mWindow->w;
00923         size->mY = mWindow->h;
00924                 return (TRUE);
00925     }
00926 
00927         llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
00928     return (FALSE);
00929 }
00930 
00931 BOOL LLWindowSDL::setPosition(const LLCoordScreen position)
00932 {
00933         if(mWindow)
00934         {
00935         // *FIX: (???)
00936                 //MacMoveWindow(mWindow, position.mX, position.mY, false);
00937         }
00938 
00939         return TRUE;
00940 }
00941 
00942 BOOL LLWindowSDL::setSize(const LLCoordScreen size)
00943 {
00944         if(mWindow)
00945         {
00946         // *FIX: (???)
00947                 //SizeWindow(mWindow, size.mX, size.mY, true);
00948         }
00949 
00950         return TRUE;
00951 }
00952 
00953 void LLWindowSDL::swapBuffers()
00954 {
00955         if (mWindow)
00956                 SDL_GL_SwapBuffers();
00957 }
00958 
00959 F32 LLWindowSDL::getGamma()
00960 {
00961         return 1/mGamma;
00962 }
00963 
00964 BOOL LLWindowSDL::restoreGamma()
00965 {
00966         //CGDisplayRestoreColorSyncSettings();
00967     SDL_SetGamma(1.0f, 1.0f, 1.0f);
00968         return true;
00969 }
00970 
00971 BOOL LLWindowSDL::setGamma(const F32 gamma)
00972 {
00973         mGamma = gamma;
00974         if (mGamma == 0) mGamma = 0.1f;
00975         mGamma = 1/mGamma;
00976         SDL_SetGamma(mGamma, mGamma, mGamma);
00977         return true;
00978 }
00979 
00980 BOOL LLWindowSDL::isCursorHidden()
00981 {
00982         return mCursorHidden;
00983 }
00984 
00985 
00986 
00987 // Constrains the mouse to the window.
00988 void LLWindowSDL::setMouseClipping( BOOL b )
00989 {
00990         //llinfos << "LLWindowSDL::setMouseClipping " << b << llendl;
00991         // Just stash the requested state.  We'll simulate this when the cursor is hidden by decoupling.
00992         mIsMouseClipping = b;
00993     //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
00994         adjustCursorDecouple();
00995 }
00996 
00997 BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position)
00998 {
00999         BOOL result = TRUE;
01000         LLCoordScreen screen_pos;
01001 
01002         if (!convertCoords(position, &screen_pos))
01003         {
01004                 return FALSE;
01005         }
01006 
01007         //llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
01008 
01009     SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
01010 
01011         // Under certain circumstances, this will trigger us to decouple the cursor.
01012         adjustCursorDecouple(true);
01013 
01014         return result;
01015 }
01016 
01017 BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position)
01018 {
01019         //Point cursor_point;
01020         LLCoordScreen screen_pos;
01021 
01022         //GetMouse(&cursor_point);
01023     int x, y;
01024     SDL_GetMouseState(&x, &y);
01025 
01026         screen_pos.mX = x;
01027         screen_pos.mY = y;
01028 
01029         return convertCoords(screen_pos, position);
01030 }
01031 
01032 void LLWindowSDL::adjustCursorDecouple(bool warpingMouse)
01033 {
01034         if(mIsMouseClipping && mCursorHidden)
01035         {
01036                 if(warpingMouse)
01037                 {
01038                         // The cursor should be decoupled.  Make sure it is.
01039                         if(!mCursorDecoupled)
01040                         {
01041                                 //                      llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
01042                                 //CGAssociateMouseAndMouseCursorPosition(false);
01043                                 mCursorDecoupled = true;
01044                                 mCursorIgnoreNextDelta = TRUE;
01045                         }
01046                 }
01047         }
01048         else
01049         {
01050                 // The cursor should not be decoupled.  Make sure it isn't.
01051                 if(mCursorDecoupled)
01052                 {
01053                         //                      llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
01054                         //CGAssociateMouseAndMouseCursorPosition(true);
01055                         mCursorDecoupled = false;
01056                 }
01057         }
01058 }
01059 
01060 F32 LLWindowSDL::getNativeAspectRatio()
01061 {
01062 #if 0
01063         // RN: this hack presumes that the largest supported resolution is monitor-limited
01064         // and that pixels in that mode are square, therefore defining the native aspect ratio
01065         // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
01066         S32 num_resolutions;
01067         LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
01068 
01069 
01070         return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
01071         //rn: AC
01072 #endif
01073 
01074         // MBW -- there are a couple of bad assumptions here.  One is that the display list won't include
01075         //              ridiculous resolutions nobody would ever use.  The other is that the list is in order.
01076 
01077         // New assumptions:
01078         // - pixels are square (the only reasonable choice, really)
01079         // - The user runs their display at a native resolution, so the resolution of the display
01080         //    when the app is launched has an aspect ratio that matches the monitor.
01081 
01082         //RN: actually, the assumption that there are no ridiculous resolutions (above the display's native capabilities) has 
01083         // been born out in my experience.  
01084         // Pixels are often not square (just ask the people who run their LCDs at 1024x768 or 800x600 when running fullscreen, like me)
01085         // The ordering of display list is a blind assumption though, so we should check for max values
01086         // Things might be different on the Mac though, so I'll defer to MBW
01087 
01088         // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
01089         // switching, and stashes it in mOriginalAspectRatio.  Here, we just return it.
01090 
01091         if (mOverrideAspectRatio > 0.f)
01092         {
01093                 return mOverrideAspectRatio;
01094         }
01095 
01096         return mOriginalAspectRatio;
01097 }
01098 
01099 F32 LLWindowSDL::getPixelAspectRatio()
01100 {
01101         F32 pixel_aspect = 1.f;
01102         if (getFullscreen())
01103         {
01104                 LLCoordScreen screen_size;
01105                 getSize(&screen_size);
01106                 pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
01107         }
01108 
01109         return pixel_aspect;
01110 }
01111 
01112 
01113 // some of this stuff is to support 'temporarily windowed' mode so that
01114 // dialogs are still usable in fullscreen.  HOWEVER! - it's not enabled/working
01115 // yet.
01116 static LLCoordScreen old_size;
01117 static BOOL old_fullscreen;
01118 void LLWindowSDL::beforeDialog()
01119 {
01120         llinfos << "LLWindowSDL::beforeDialog()" << llendl;
01121 
01122         if (SDLReallyCaptureInput(FALSE) // must ungrab input so popup works!
01123             && getSize(&old_size))
01124         {
01125                 old_fullscreen = was_fullscreen;
01126                 
01127                 if (old_fullscreen)
01128                 {
01129                         // NOT YET WORKING
01130                         //switchContext(FALSE, old_size, TRUE);
01131                 }
01132         }
01133 
01134 #if LL_X11
01135         if (mSDL_Display)
01136         {
01137                 // Everything that we/SDL asked for should happen before we
01138                 // potentially hand control over to GTK.
01139                 maybe_lock_display();
01140                 XSync(mSDL_Display, False);
01141                 maybe_unlock_display();
01142         }
01143 #endif // LL_X11
01144 
01145 #if LL_GTK
01146         // this is a good time to grab some GTK version information for
01147         // diagnostics, if not already done.
01148         ll_try_gtk_init();
01149 #endif // LL_GTK
01150 
01151         maybe_lock_display();
01152 }
01153 
01154 void LLWindowSDL::afterDialog()
01155 {
01156         llinfos << "LLWindowSDL::afterDialog()" << llendl;
01157 
01158         maybe_unlock_display();
01159 
01160         if (old_fullscreen && !was_fullscreen)
01161         {
01162                 // *FIX: NOT YET WORKING (see below)
01163                 //switchContext(TRUE, old_size, TRUE);
01164         }
01165         // *FIX: we need to restore the GL context using
01166         // LLViewerWindow::restoreGL() - but how??
01167 }
01168 
01169 
01170 S32 LLWindowSDL::stat(const char* file_name, struct stat* stat_info)
01171 {
01172         return ::stat( file_name, stat_info );
01173 }
01174 
01175 #if LL_X11
01176 // set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
01177 void LLWindowSDL::x11_set_urgent(BOOL urgent)
01178 {
01179         if (mSDL_Display && !mFullscreen)
01180         {
01181                 XWMHints *wm_hints;
01182                 
01183                 llinfos << "X11 hint for urgency, " << urgent << llendl;
01184 
01185                 maybe_lock_display();
01186                 wm_hints = XGetWMHints(mSDL_Display, mSDL_XWindowID);
01187                 if (!wm_hints)
01188                         wm_hints = XAllocWMHints();
01189 
01190                 if (urgent)
01191                         wm_hints->flags |= XUrgencyHint;
01192                 else
01193                         wm_hints->flags &= ~XUrgencyHint;
01194 
01195                 XSetWMHints(mSDL_Display, mSDL_XWindowID, wm_hints);
01196                 XFree(wm_hints);
01197                 XSync(mSDL_Display, False);
01198                 maybe_unlock_display();
01199         }
01200 }
01201 #endif // LL_X11
01202 
01203 void LLWindowSDL::flashIcon(F32 seconds)
01204 {
01205 #if !LL_X11
01206         llinfos << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
01207 #else   
01208         llinfos << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
01209         
01210         F32 remaining_time = mFlashTimer.getRemainingTimeF32();
01211         if (remaining_time < seconds)
01212                 remaining_time = seconds;
01213         mFlashTimer.reset();
01214         mFlashTimer.setTimerExpirySec(remaining_time);
01215 
01216         x11_set_urgent(TRUE);
01217         mFlashing = TRUE;
01218 #endif // LL_X11
01219 }
01220 
01221 #if LL_X11
01222 /* Lots of low-level X11 stuff to handle X11 copy-and-paste */
01223 
01224 /* Our X11 clipboard support is a bit bizarre in various
01225    organically-grown ways.  Ideally it should be fixed to do
01226    real string-type negotiation (this would make pasting to
01227    xterm faster and pasting to UTF-8 emacs work properly), but
01228    right now it has the rare and desirable trait of being
01229    generally stable and working. */
01230 
01231 typedef Atom x11clipboard_type;
01232 
01233 /* PRIMARY and CLIPBOARD are the two main kinds of
01234    X11 clipboard.  A third are the CUT_BUFFERs which an
01235    obsolete holdover from X10 days and use a quite orthogonal
01236    mechanism.  CLIPBOARD is the type whose design most
01237    closely matches SL's own win32-alike explicit copy-and-paste
01238    paradigm.
01239 
01240    Pragmatically we support all three to varying degrees.  When
01241    we paste into SL, it is strictly from CLIPBOARD.  When we copy,
01242    we support (to as full an extent as the clipboard content type
01243    allows) CLIPBOARD, PRIMARY, and CUT_BUFFER0.
01244  */
01245 static x11clipboard_type get_x11_readwrite_clipboard_type(void)
01246 {
01247         return XInternAtom(get_SDL_Display(), "CLIPBOARD", False);
01248 }
01249 
01250 static x11clipboard_type get_x11_write_clipboard_type(void)
01251 {
01252         return XA_PRIMARY;
01253 }
01254 
01255 /* This is where our own private cutbuffer goes - we don't use
01256    a regular cutbuffer (XA_CUT_BUFFER0 etc) for intermediate
01257    storage because their use isn't really defined for holding UTF8. */
01258 static x11clipboard_type get_x11_cutbuffer_clipboard_type(void)
01259 {
01260         return XInternAtom(get_SDL_Display(), "SECONDLIFE_CUTBUFFER", False);
01261 }
01262 
01263 /* Some X11 atom-generators */
01264 static Atom get_x11_targets_atom(void)
01265 {
01266         return XInternAtom(get_SDL_Display(), "TARGETS", False);
01267 }
01268 
01269 static Atom get_x11_text_atom(void)
01270 {
01271         return XInternAtom(get_SDL_Display(), "TEXT", False);
01272 }
01273 
01274 /* These defines, and convert_data/convert_x11clipboard,
01275    mostly exist to support non-text or unusually-encoded
01276    clipboard data, which we don't really have a need for at
01277    the moment. */
01278 #define SDLCLIPTYPE(A, B, C, D) (int)(((A)<<24)|((B)<<16)|((C)<<8)|((D)<<0))
01279 #define FORMAT_PREFIX   "SECONDLIFE_x11clipboard_0x"
01280 
01281 static
01282 x11clipboard_type convert_format(int type)
01283 {
01284         if (!gWindowImplementation)
01285         {
01286                 llwarns << "!gWindowImplementation in convert_format()"
01287                         << llendl;
01288                 return XA_STRING;
01289         }
01290 
01291         switch (type)
01292         {
01293         case SDLCLIPTYPE('T', 'E', 'X', 'T'):
01294                 // old-style X11 clipboard, strictly only ISO 8859-1 encoding
01295                 return XA_STRING;
01296         case SDLCLIPTYPE('U', 'T', 'F', '8'):
01297                 // newer de-facto UTF8 clipboard atom
01298                 return XInternAtom(gWindowImplementation->mSDL_Display,
01299                                    "UTF8_STRING", False);
01300         default:
01301         {
01302                 /* completely arbitrary clipboard types... we don't actually use
01303                 these right now, and support is skeletal. */
01304                 char format[sizeof(FORMAT_PREFIX)+8+1]; /* Flawfinder: ignore */
01305 
01306                 snprintf(format, sizeof(format), "%s%08lx", FORMAT_PREFIX, (unsigned long)type);
01307                 return XInternAtom(gWindowImplementation->mSDL_Display,
01308                                    format, False);
01309         }
01310     }
01311 }
01312 
01313 /* convert platform string to x11 clipboard format.  for our
01314    purposes this is pretty trivial right now. */
01315 static int
01316 convert_data(int type, char *dst, const char *src, int srclen)
01317 {
01318         int dstlen;
01319 
01320         dstlen = 0;
01321         switch (type)
01322         {
01323         case SDLCLIPTYPE('T', 'E', 'X', 'T'):
01324         case SDLCLIPTYPE('U', 'T', 'F', '8'):
01325                 if (src == NULL)
01326                 {
01327                         break;
01328                 }
01329                 if ( srclen == 0 )
01330                         srclen = strlen(src);   /* Flawfinder: ignore */
01331                 
01332                 dstlen = srclen + 1;
01333                 
01334                 if ( dst ) // assume caller made it big enough by asking us
01335                 {
01336                         memcpy(dst, src, srclen);       /* Flawfinder: ignore */
01337                         dst[srclen] = '\0';
01338                 }
01339                 break;
01340                 
01341         default:
01342                 llwarns << "convert_data: Unknown medium type" << llendl;
01343                 break;
01344         }
01345         return(dstlen);
01346 }
01347 
01348 /* Convert x11clipboard data to platform string.  This too is
01349    pretty trivial for our needs right now, and just about identical
01350    to above. */
01351 static int
01352 convert_x11clipboard(int type, char *dst, const char *src, int srclen)
01353 {
01354         int dstlen;
01355 
01356         dstlen = 0;
01357         switch (type)
01358         {
01359         case SDLCLIPTYPE('U', 'T', 'F', '8'):
01360         case SDLCLIPTYPE('T', 'E', 'X', 'T'):
01361                 if (src == NULL)
01362                 {
01363                         break;
01364                 }
01365                 if ( srclen == 0 )
01366                         srclen = strlen(src);   /* Flawfinder: ignore */
01367                 
01368                 dstlen = srclen + 1;
01369                 
01370                 if ( dst ) // assume caller made it big enough by asking us
01371                 {
01372                         memcpy(dst, src, srclen);       /* Flawfinder: ignore */
01373                         dst[srclen] = '\0';
01374                 }
01375                 break;
01376                 
01377         default:
01378                 llwarns << "convert_x11clipboard: Unknown medium type" << llendl;
01379                 break;
01380         }
01381         return dstlen;
01382 }
01383 
01384 int
01385 LLWindowSDL::is_empty_x11clipboard(void)
01386 {
01387         int retval;
01388 
01389         maybe_lock_display();
01390         retval = ( XGetSelectionOwner(mSDL_Display, get_x11_readwrite_clipboard_type()) == None );
01391         maybe_unlock_display();
01392 
01393         return(retval);
01394 }
01395 
01396 void
01397 LLWindowSDL::put_x11clipboard(int type, int srclen, const char *src)
01398 {
01399         x11clipboard_type format;
01400         int dstlen;
01401         char *dst;
01402 
01403         format = convert_format(type);
01404         dstlen = convert_data(type, NULL, src, srclen);
01405 
01406         dst = (char *)malloc(dstlen);
01407         if ( dst != NULL )
01408         {
01409                 maybe_lock_display();
01410                 Window root = DefaultRootWindow(mSDL_Display);
01411                 convert_data(type, dst, src, srclen);
01412                 // Cutbuffers are only allowed to have STRING atom types,
01413                 // but Emacs puts UTF8 inside them anyway.  We cautiously
01414                 // don't.
01415                 if (type == SDLCLIPTYPE('T','E','X','T'))
01416                 {
01417                         // dstlen-1 so we don't include the trailing \0
01418                         llinfos << "X11: Populating cutbuffer." <<llendl;
01419                         XChangeProperty(mSDL_Display, root,
01420                                         XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace,
01421                                         (unsigned char*)dst, dstlen-1);
01422                 } else {
01423                         // Should we clear the cutbuffer if we can't put the selection in
01424                         // it because it's a UTF8 selection?  Eh, no great reason I think.
01425                         //XDeleteProperty(SDL_Display, root, XA_CUT_BUFFER0);
01426                 }
01427                 // Private cutbuffer of an appropriate type.
01428                 XChangeProperty(mSDL_Display, root,
01429                                 get_x11_cutbuffer_clipboard_type(), format, 8, PropModeReplace,
01430                                 (unsigned char*)dst, dstlen-1);
01431                 free(dst);
01432                 
01433                 /* Claim ownership of both PRIMARY and CLIPBOARD */
01434                 XSetSelectionOwner(mSDL_Display, get_x11_readwrite_clipboard_type(),
01435                                    mSDL_XWindowID, CurrentTime);
01436                 XSetSelectionOwner(mSDL_Display, get_x11_write_clipboard_type(),
01437                                    mSDL_XWindowID, CurrentTime);
01438                 
01439                 maybe_unlock_display();
01440         }
01441 }
01442 
01443 void
01444 LLWindowSDL::get_x11clipboard(int type, int *dstlen, char **dst)
01445 {
01446         x11clipboard_type format;
01447         
01448         *dstlen = 0;
01449         format = convert_format(type);
01450 
01451         Window owner;
01452         Atom selection;
01453         Atom seln_type;
01454         int seln_format;
01455         unsigned long nbytes;
01456         unsigned long overflow;
01457         char *src;
01458         
01459         maybe_lock_display();
01460         owner = XGetSelectionOwner(mSDL_Display, get_x11_readwrite_clipboard_type());
01461         maybe_unlock_display();
01462         if (owner == None)
01463         {
01464                 // Fall right back to ancient X10 cut-buffers
01465                 owner = DefaultRootWindow(mSDL_Display);
01466                 selection = XA_CUT_BUFFER0;
01467         } else if (owner == mSDL_XWindowID)
01468         {
01469                 // Use our own uncooked opaque string property
01470                 owner = DefaultRootWindow(mSDL_Display);
01471                 selection = get_x11_cutbuffer_clipboard_type();
01472         }
01473         else
01474         {
01475                 // Use full-on X11-style clipboard negotiation with the owning app
01476                 int selection_response = 0;
01477                 SDL_Event event;
01478                 
01479                 owner = mSDL_XWindowID;
01480                 maybe_lock_display();
01481                 selection = XInternAtom(mSDL_Display, "SDL_SELECTION", False);
01482                 XConvertSelection(mSDL_Display, get_x11_readwrite_clipboard_type(), format,
01483                                   selection, owner, CurrentTime);
01484                 maybe_unlock_display();
01485                 llinfos << "X11: Waiting for clipboard to arrive." <<llendl;
01486                 while ( ! selection_response )
01487                 {
01488                         // Only look for SYSWMEVENTs, or we may lose keypresses
01489                         // etc.
01490                         SDL_PumpEvents();
01491                         if (1 == SDL_PeepEvents(&event, 1, SDL_GETEVENT,
01492                                                 SDL_SYSWMEVENTMASK) )
01493                         {
01494                                 if ( event.type == SDL_SYSWMEVENT )
01495                                 {
01496                                         XEvent xevent =
01497                                                 event.syswm.msg->event.xevent;
01498                                 
01499                                         if ( (xevent.type == SelectionNotify)&&
01500                                              (xevent.xselection.requestor == owner) )
01501                                                 selection_response = 1;
01502                                 }
01503                         } else {
01504                                 llinfos << "X11: Waiting for SYSWM event..." <<  llendl;
01505                         }
01506                 }
01507                 llinfos << "X11: Clipboard arrived." <<llendl;
01508         }
01509 
01510         maybe_lock_display();
01511         if ( XGetWindowProperty(mSDL_Display, owner, selection, 0, INT_MAX/4,
01512                                 False, format, &seln_type, &seln_format,
01513                                 &nbytes, &overflow, (unsigned char **)&src) == Success )
01514         {
01515                 if ( seln_type == format )
01516                 {
01517                         *dstlen = convert_x11clipboard(type, NULL, src, nbytes);
01518                         *dst = (char *)realloc(*dst, *dstlen);
01519                         if ( *dst == NULL )
01520                                 *dstlen = 0;
01521                         else
01522                                 convert_x11clipboard(type, *dst, src, nbytes);
01523                 }
01524                 XFree(src);
01525         }
01526         maybe_unlock_display();
01527 }
01528 
01529 int clipboard_filter_callback(const SDL_Event *event)
01530 {
01531         /* Post all non-window manager specific events */
01532         if ( event->type != SDL_SYSWMEVENT )
01533         {
01534                 return(1);
01535         }
01536 
01537         /* Handle window-manager specific clipboard events */
01538         switch (event->syswm.msg->event.xevent.type) {
01539         /* Copy the selection from SECONDLIFE_CUTBUFFER to the requested property */
01540         case SelectionRequest: {
01541                 XSelectionRequestEvent *req;
01542                 XEvent sevent;
01543                 int seln_format;
01544                 unsigned long nbytes;
01545                 unsigned long overflow;
01546                 unsigned char *seln_data;
01547 
01548                 req = &event->syswm.msg->event.xevent.xselectionrequest;
01549                 sevent.xselection.type = SelectionNotify;
01550                 sevent.xselection.display = req->display;
01551                 sevent.xselection.selection = req->selection;
01552                 sevent.xselection.target = None;
01553                 sevent.xselection.property = None;
01554                 sevent.xselection.requestor = req->requestor;
01555                 sevent.xselection.time = req->time;
01556                 if ( XGetWindowProperty(get_SDL_Display(), DefaultRootWindow(get_SDL_Display()),
01557                                         get_x11_cutbuffer_clipboard_type(), 0, INT_MAX/4, False, req->target,
01558                                         &sevent.xselection.target, &seln_format,
01559                                         &nbytes, &overflow, &seln_data) == Success )
01560                 {
01561                         if ( sevent.xselection.target == req->target)
01562                         {
01563                                 if ( sevent.xselection.target == XA_STRING ||
01564                                      sevent.xselection.target ==
01565                                      convert_format(SDLCLIPTYPE('U','T','F','8')) )
01566                                 {
01567                                         if ( seln_data[nbytes-1] == '\0' )
01568                                                 --nbytes;
01569                                 }
01570                                 XChangeProperty(get_SDL_Display(), req->requestor, req->property,
01571                                                 req->target, seln_format, PropModeReplace,
01572                                                 seln_data, nbytes);
01573                                 sevent.xselection.property = req->property;
01574                         } else if (get_x11_targets_atom() == req->target) {
01575                                 /* only advertise what we currently support */
01576                                 const int num_supported = 3;
01577                                 Atom supported[num_supported] = {
01578                                         XA_STRING, // will be over-written below
01579                                         get_x11_text_atom(),
01580                                         get_x11_targets_atom()
01581                                 };
01582                                 supported[0] = sevent.xselection.target;
01583                                 XChangeProperty(get_SDL_Display(), req->requestor,
01584                                                 req->property, XA_ATOM, 32, PropModeReplace,
01585                                                 (unsigned char*)supported,
01586                                                 num_supported);
01587                                 sevent.xselection.property = req->property;
01588                                 llinfos << "Clipboard: An app asked us what selections format we offer." << llendl;
01589                         } else {
01590                                 llinfos << "Clipboard: An app requested an unsupported selection format " << req->target << ", we have " << sevent.xselection.target << llendl;
01591                             sevent.xselection.target = None;
01592                         }
01593                         XFree(seln_data);
01594                 }
01595                 int sendret =
01596                         XSendEvent(get_SDL_Display(),req->requestor,False,0,&sevent);
01597                 if ((sendret==BadValue) || (sendret==BadWindow))
01598                         llwarns << "Clipboard SendEvent failed" << llendl;
01599                 XSync(get_SDL_Display(), False);
01600         }
01601                 break;
01602         }
01603         
01604         /* Post the event for X11 clipboard reading above */
01605         return(1);
01606 }
01607 
01608 int
01609 LLWindowSDL::init_x11clipboard(void)
01610 {
01611         SDL_SysWMinfo info;
01612         int retval;
01613 
01614         /* Grab the window manager specific information */
01615         retval = -1;
01616         SDL_SetError("SDL is not running on known window manager");
01617 
01618         SDL_VERSION(&info.version);
01619         if ( SDL_GetWMInfo(&info) )
01620         {
01621                 /* Save the information for later use */
01622                 if ( info.subsystem == SDL_SYSWM_X11 )
01623                 {
01624                         mSDL_Display = info.info.x11.display;
01625                         mSDL_XWindowID = info.info.x11.wmwindow;
01626                         Lock_Display = info.info.x11.lock_func;
01627                         Unlock_Display = info.info.x11.unlock_func;
01628                         
01629                         /* Enable the special window hook events */
01630                         SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
01631                         SDL_SetEventFilter(clipboard_filter_callback);
01632                         
01633                         retval = 0;
01634                 }
01635                 else
01636                 {
01637                         SDL_SetError("SDL is not running on X11");
01638                 }
01639         }
01640         return(retval);
01641 }
01642 
01643 void
01644 LLWindowSDL::quit_x11clipboard(void)
01645 {
01646         mSDL_Display = NULL;
01647         mSDL_XWindowID = None;
01648         Lock_Display = NULL;
01649         Unlock_Display = NULL;
01650 
01651         SDL_SetEventFilter(NULL); // Stop custom event filtering
01652 }
01653 
01654 /************************************************/
01655 
01656 BOOL LLWindowSDL::isClipboardTextAvailable()
01657 {
01658         return !is_empty_x11clipboard();
01659 }
01660 
01661 BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
01662 {
01663         int cliplen; // seems 1 or 2 bytes longer than expected
01664         char *cliptext = NULL;
01665         get_x11clipboard(SDLCLIPTYPE('U','T','F','8'), &cliplen, &cliptext);
01666         if (cliptext)
01667         {
01668                 llinfos << "X11: Got UTF8 clipboard text." << llendl;
01669                 // at some future time we can use cliplen instead of relying on \0,
01670                 // if we ever grok non-ascii, non-utf8 encodings on the clipboard.
01671                 std::string clip_str(cliptext);
01672                 // we can't necessarily trust the incoming text to be valid UTF-8,
01673                 // but utf8str_to_wstring() seems to do an appropriate level of
01674                 // validation for avoiding over-reads.
01675                 dst = utf8str_to_wstring(clip_str);
01676                 /*llinfos << "X11 pasteTextFromClipboard: cliplen=" << cliplen <<
01677                         " strlen(cliptext)=" << strlen(cliptext) <<
01678                         " clip_str.length()=" << clip_str.length() <<
01679                         " dst.length()=" << dst.length() <<
01680                         llendl;*/
01681                 free(cliptext);
01682                 return TRUE; // success
01683         }
01684         get_x11clipboard(SDLCLIPTYPE('T','E','X','T'), &cliplen, &cliptext);
01685         if (cliptext)
01686         {
01687                 llinfos << "X11: Got ISO 8859-1 clipboard text." << llendl;
01688                 std::string clip_str(cliptext);
01689                 std::string utf8_str = rawstr_to_utf8(clip_str);
01690                 dst = utf8str_to_wstring(utf8_str);
01691                 free(cliptext);
01692         }
01693         return FALSE; // failure
01694 }
01695 
01696 BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
01697 {
01698         std::string utf8text = wstring_to_utf8str(s);
01699         const char* cstr = utf8text.c_str();
01700         if (cstr == NULL)
01701         {
01702                 return FALSE;
01703         }
01704         int cstrlen = strlen(cstr);     /* Flawfinder: ignore */
01705         int i;
01706         for (i=0; i<cstrlen; ++i)
01707         {
01708                 if (0x80 & (unsigned char)cstr[i])
01709                 {
01710                         // Found an 8-bit character; use new-style UTF8 clipboard
01711                         llinfos << "X11: UTF8 copyTextToClipboard" << llendl;
01712                         put_x11clipboard(SDLCLIPTYPE('U','T','F','8'), cstrlen, cstr);
01713                         return TRUE;
01714                 }
01715         }
01716         // Didn't find any 8-bit characters; use old-style ISO 8859-1 clipboard
01717         llinfos << "X11: ISO 8859-1 copyTextToClipboard" << llendl;
01718         put_x11clipboard(SDLCLIPTYPE('T','E','X','T'), cstrlen, cstr);
01719         return TRUE;
01720 }
01721 #else
01722 
01723 BOOL LLWindowSDL::isClipboardTextAvailable()
01724 {
01725         return FALSE; // unsupported
01726 }
01727 
01728 BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
01729 {
01730         return FALSE; // unsupported
01731 }
01732 
01733 BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
01734 {
01735         return FALSE;  // unsupported
01736 }
01737 #endif // LL_X11
01738 
01739 BOOL LLWindowSDL::sendEmail(const char* address, const char* subject, const char* body_text,
01740                                                                            const char* attachment, const char* attachment_displayed_name )
01741 {
01742         // MBW -- XXX -- Um... yeah.  I'll get to this later.
01743 
01744         return FALSE;
01745 }
01746 
01747 
01748 LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
01749 {
01750         if (!mSupportedResolutions)
01751         {
01752                 mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
01753                 mNumSupportedResolutions = 0;
01754 
01755         SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
01756         if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
01757         {
01758             int count = 0;
01759             while (*modes)  // they're sorted biggest to smallest, so find end...
01760             {
01761                 modes++;
01762                 count++;
01763             }
01764 
01765             while (count--)
01766             {
01767                 modes--;
01768                 SDL_Rect *r = *modes;
01769                 int w = r->w;
01770                 int h = r->h;
01771                 if ((w >= 800) && (h >= 600))
01772                 {
01773                     // make sure we don't add the same resolution multiple times!
01774                     if ( (mNumSupportedResolutions == 0) ||
01775                          ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
01776                           (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
01777                     {
01778                         mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
01779                         mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
01780                         mNumSupportedResolutions++;
01781                     }
01782                 }
01783             }
01784         }
01785         }
01786 
01787         num_resolutions = mNumSupportedResolutions;
01788         return mSupportedResolutions;
01789 }
01790 
01791 BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
01792 {
01793     if (!to)
01794         return FALSE;
01795 
01796         to->mX = from.mX;
01797         to->mY = mWindow->h - from.mY - 1;
01798 
01799         return TRUE;
01800 }
01801 
01802 BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
01803 {
01804     if (!to)
01805         return FALSE;
01806 
01807         to->mX = from.mX;
01808         to->mY = mWindow->h - from.mY - 1;
01809 
01810         return TRUE;
01811 }
01812 
01813 BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
01814 {
01815     if (!to)
01816                 return FALSE;
01817 
01818         // In the fullscreen case, window and screen coordinates are the same.
01819         to->mX = from.mX;
01820         to->mY = from.mY;
01821     return (TRUE);
01822 }
01823 
01824 BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
01825 {
01826     if (!to)
01827                 return FALSE;
01828 
01829         // In the fullscreen case, window and screen coordinates are the same.
01830         to->mX = from.mX;
01831         to->mY = from.mY;
01832     return (TRUE);
01833 }
01834 
01835 BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
01836 {
01837         LLCoordWindow window_coord;
01838 
01839         return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
01840 }
01841 
01842 BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
01843 {
01844         LLCoordWindow window_coord;
01845 
01846         return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
01847 }
01848 
01849 
01850 
01851 
01852 void LLWindowSDL::setupFailure(const char* text, const char* caption, U32 type)
01853 {
01854         destroyContext();
01855 
01856         OSMessageBox(text, caption, type);
01857 }
01858 
01859 BOOL LLWindowSDL::SDLReallyCaptureInput(BOOL capture)
01860 {
01861         // note: this used to be safe to call nestedly, but in the
01862         // end that's not really a wise usage pattern, so don't.
01863 
01864         if (capture)
01865                 mReallyCapturedCount = 1;
01866         else
01867                 mReallyCapturedCount = 0;
01868         
01869         SDL_GrabMode wantmode, newmode;
01870         if (mReallyCapturedCount <= 0) // uncapture
01871         {
01872                 wantmode = SDL_GRAB_OFF;
01873         } else // capture
01874         {
01875                 wantmode = SDL_GRAB_ON;
01876         }
01877         
01878         if (mReallyCapturedCount < 0) // yuck, imbalance.
01879         {
01880                 mReallyCapturedCount = 0;
01881                 llwarns << "ReallyCapture count was < 0" << llendl;
01882         }
01883 
01884         if (!mFullscreen) /* only bother if we're windowed anyway */
01885         {
01886 #if LL_X11
01887                 if (mSDL_Display)
01888                 {
01889                         /* we dirtily mix raw X11 with SDL so that our pointer
01890                            isn't (as often) constrained to the limits of the
01891                            window while grabbed, which feels nicer and
01892                            hopefully eliminates some reported 'sticky pointer'
01893                            problems.  We use raw X11 instead of
01894                            SDL_WM_GrabInput() because the latter constrains
01895                            the pointer to the window and also steals all
01896                            *keyboard* input from the window manager, which was
01897                            frustrating users. */
01898                         int result;
01899                         if (wantmode == SDL_GRAB_ON)
01900                         {
01901                                 //llinfos << "X11 POINTER GRABBY" << llendl;
01902                                 //newmode = SDL_WM_GrabInput(wantmode);
01903                                 maybe_lock_display();
01904                                 result = XGrabPointer(mSDL_Display, mSDL_XWindowID,
01905                                                       True, 0, GrabModeAsync,
01906                                                       GrabModeAsync,
01907                                                       None, None, CurrentTime);
01908                                 maybe_unlock_display();
01909                                 if (GrabSuccess == result)
01910                                         newmode = SDL_GRAB_ON;
01911                                 else
01912                                         newmode = SDL_GRAB_OFF;
01913                         } else if (wantmode == SDL_GRAB_OFF)
01914                         {
01915                                 //llinfos << "X11 POINTER UNGRABBY" << llendl;
01916                                 newmode = SDL_GRAB_OFF;
01917                                 //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
01918                                 
01919                                 maybe_lock_display();
01920                                 XUngrabPointer(mSDL_Display, CurrentTime);
01921                                 // Make sure the ungrab happens RIGHT NOW.
01922                                 XSync(mSDL_Display, False);
01923                                 maybe_unlock_display();
01924                         } else
01925                         {
01926                                 newmode = SDL_GRAB_QUERY; // neutral
01927                         }
01928                 } else // not actually running on X11, for some reason
01929                         newmode = wantmode;
01930 #endif // LL_X11
01931         } else {
01932                 // pretend we got what we wanted, when really we don't care.
01933                 newmode = wantmode;
01934         }
01935         
01936         // return boolean success for whether we ended up in the desired state
01937         return (capture && SDL_GRAB_ON==newmode) ||
01938                 (!capture && SDL_GRAB_OFF==newmode);
01939 }
01940 
01941 U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain)
01942 {
01943         /* part of the fix for SL-13243: Some popular window managers like
01944            to totally eat alt-drag for the purposes of moving windows.  We
01945            spoil their day by acquiring the exclusive X11 mouse lock for as
01946            long as LALT is held down, so the window manager can't easily
01947            see what's happening.  Tested successfully with Metacity.
01948            And... do the same with CTRL, for other darn WMs.  We don't
01949            care about other metakeys as SL doesn't use them with dragging
01950            (for now). */
01951 
01952         /* We maintain a bitmap of critical keys which are up and down
01953            instead of simply key-counting, because SDL sometimes reports
01954            misbalanced keyup/keydown event pairs to us for whatever reason. */
01955 
01956         U32 mask = 0;
01957         switch (keysym)
01958         {
01959         case SDLK_LALT:
01960                 mask = 1U << 0; break;
01961         case SDLK_LCTRL:
01962                 mask = 1U << 1; break;
01963         case SDLK_RCTRL:
01964                 mask = 1U << 2; break;
01965         default:
01966                 break;
01967         }
01968 
01969         if (gain)
01970                 mGrabbyKeyFlags |= mask;
01971         else
01972                 mGrabbyKeyFlags &= ~mask;
01973 
01974         //llinfos << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << llendl;
01975 
01976         /* 0 means we don't need to mousegrab, otherwise grab. */
01977         return mGrabbyKeyFlags;
01978 }
01979 
01980 void LLWindowSDL::gatherInput()
01981 {
01982     const Uint32 CLICK_THRESHOLD = 300;  // milliseconds
01983     static int leftClick = 0;
01984     static int rightClick = 0;
01985     static Uint32 lastLeftDown = 0;
01986     static Uint32 lastRightDown = 0;
01987     SDL_Event event;
01988 
01989 #if LL_GTK && LL_LIBXUL_ENABLED
01990     // Pump GTK events so embedded Gecko doesn't starve.
01991     if (ll_try_gtk_init())
01992     {
01993             // Yuck, Mozilla's GTK callbacks play with the locale - push/pop
01994             // the locale to protect it, as exotic/non-C locales
01995             // causes our code lots of general critical weirdness
01996             // and crashness. (SL-35450)
01997             std::string saved_locale = setlocale(LC_ALL, NULL);
01998 
01999             // Do a limited number of pumps so SL doesn't starve!
02000             // *TODO: this should ideally be time-limited, not count-limited.
02001             gtk_main_iteration_do(0); // Always do one non-blocking pump
02002             for (int iter=0; iter<10; ++iter)
02003                     if (gtk_events_pending())
02004                             gtk_main_iteration();
02005 
02006             setlocale(LC_ALL, saved_locale.c_str() );
02007     }
02008 #endif // LL_GTK && LL_LIBXUL_ENABLED
02009 
02010     // Handle all outstanding SDL events
02011     while (SDL_PollEvent(&event))
02012     {
02013         switch (event.type)
02014         {
02015             case SDL_MOUSEMOTION:
02016             {
02017                 LLCoordWindow winCoord(event.button.x, event.button.y);
02018                 LLCoordGL openGlCoord;
02019                 convertCoords(winCoord, &openGlCoord);
02020                                 MASK mask = gKeyboard->currentMask(TRUE);
02021                                 mCallbacks->handleMouseMove(this, openGlCoord, mask);
02022                 break;
02023             }
02024 
02025             case SDL_KEYDOWN:
02026                 gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod);
02027                 // part of the fix for SL-13243
02028                 if (SDLCheckGrabbyKeys(event.key.keysym.sym, TRUE) != 0)
02029                         SDLReallyCaptureInput(TRUE);
02030 
02031                 if (event.key.keysym.unicode)
02032                                 {
02033                                         handleUnicodeUTF16(event.key.keysym.unicode,
02034                                                                            gKeyboard->currentMask(FALSE));
02035                                 }
02036                 break;
02037 
02038             case SDL_KEYUP:
02039                 if (SDLCheckGrabbyKeys(event.key.keysym.sym, FALSE) == 0)
02040                         SDLReallyCaptureInput(FALSE); // part of the fix for SL-13243
02041 
02042                 gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod);
02043                 break;
02044 
02045             case SDL_MOUSEBUTTONDOWN:
02046             {
02047                 bool isDoubleClick = false;
02048                 LLCoordWindow winCoord(event.button.x, event.button.y);
02049                 LLCoordGL openGlCoord;
02050                 convertCoords(winCoord, &openGlCoord);
02051                 MASK mask = gKeyboard->currentMask(TRUE);
02052 
02053                 if (event.button.button == SDL_BUTTON_LEFT)   // SDL doesn't manage double clicking...
02054                 {
02055                     Uint32 now = SDL_GetTicks();
02056                     if ((now - lastLeftDown) > CLICK_THRESHOLD)
02057                         leftClick = 1;
02058                     else
02059                     {
02060                         if (++leftClick >= 2)
02061                         {
02062                             leftClick = 0;
02063                             isDoubleClick = true;
02064                         }
02065                     }
02066                     lastLeftDown = now;
02067                 }
02068                 else if (event.button.button == SDL_BUTTON_RIGHT)
02069                 {
02070                     Uint32 now = SDL_GetTicks();
02071                     if ((now - lastRightDown) > CLICK_THRESHOLD)
02072                         rightClick = 1;
02073                     else
02074                     {
02075                         if (++rightClick >= 2)
02076                         {
02077                             rightClick = 0;
02078                                             isDoubleClick = true;
02079                         }
02080                     }
02081                     lastRightDown = now;
02082                 }
02083 
02084                 if (event.button.button == SDL_BUTTON_LEFT)  // left
02085                 {
02086                     if (isDoubleClick)
02087                                         mCallbacks->handleDoubleClick(this, openGlCoord, mask);
02088                     else
02089                                     mCallbacks->handleMouseDown(this, openGlCoord, mask);
02090                 }
02091 
02092                 else if (event.button.button == SDL_BUTTON_RIGHT)  // right ... yes, it's 3, not 2, in SDL...
02093                 {
02094                     // right double click isn't handled right now in Second Life ... if (isDoubleClick)
02095                                     mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
02096                 }
02097 
02098                 else if (event.button.button == SDL_BUTTON_MIDDLE)  // middle
02099                                 {
02100                                     mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
02101                                 }
02102                 else if (event.button.button == 4)  // mousewheel up...thanks to X11 for making SDL consider these "buttons".
02103                                         mCallbacks->handleScrollWheel(this, -1);
02104                 else if (event.button.button == 5)  // mousewheel down...thanks to X11 for making SDL consider these "buttons".
02105                                         mCallbacks->handleScrollWheel(this, 1);
02106 
02107                 break;
02108             }
02109 
02110             case SDL_MOUSEBUTTONUP:
02111             {
02112                 LLCoordWindow winCoord(event.button.x, event.button.y);
02113                 LLCoordGL openGlCoord;
02114                 convertCoords(winCoord, &openGlCoord);
02115                 MASK mask = gKeyboard->currentMask(TRUE);
02116 
02117                 if (event.button.button == SDL_BUTTON_LEFT)  // left
02118                                     mCallbacks->handleMouseUp(this, openGlCoord, mask);
02119                 else if (event.button.button == SDL_BUTTON_RIGHT)  // right ... yes, it's 3, not 2, in SDL...
02120                                     mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
02121                 else if (event.button.button == SDL_BUTTON_MIDDLE)  // middle
02122                 {
02123                         mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
02124                 }
02125                 // don't handle mousewheel here...
02126 
02127                 break;
02128             }
02129 
02130             case SDL_VIDEOEXPOSE:  // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing!
02131                             mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h);
02132                 break;
02133 
02134             case SDL_VIDEORESIZE:  // *FIX: handle this?
02135                 llinfos << "Handling a resize event: " << event.resize.w <<
02136                         "x" << event.resize.h << llendl;
02137 
02138                 // *FIX: I'm not sure this is necessary!
02139                 mWindow = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, mSDLFlags);
02140                 if (!mWindow)
02141                 {
02142                         // *FIX: More informative dialog?
02143                         llinfos << "Could not recreate context after resize! Quitting..." << llendl;
02144                         if(mCallbacks->handleCloseRequest(this))
02145                         {
02146                                 // Get the app to initiate cleanup.
02147                                 mCallbacks->handleQuit(this);
02148                                 // The app is responsible for calling destroyWindow when done with GL
02149                         }
02150                 break;
02151                 }
02152                 
02153                 mCallbacks->handleResize(this, event.resize.w, event.resize.h );
02154                 break;
02155 
02156             case SDL_ACTIVEEVENT:
02157                 if (event.active.state & SDL_APPINPUTFOCUS)
02158                 {
02159                         // Note that for SDL (particularly on X11), keyboard
02160                         // and mouse focus are independent things.  Here we are
02161                         // tracking keyboard focus state changes.
02162 
02163                         // We have to do our own state massaging because SDL
02164                         // can send us two unfocus events in a row for example,
02165                         // which confuses the focus code [SL-24071].
02166                         if (event.active.gain != mHaveInputFocus)
02167                         {
02168                                 if (event.active.gain)
02169                                         mCallbacks->handleFocus(this);
02170                                 else
02171                                         mCallbacks->handleFocusLost(this);
02172                         
02173                                 mHaveInputFocus = !!event.active.gain;
02174                         }
02175                 }
02176                 if (event.active.state & SDL_APPACTIVE)
02177                 {
02178                         // Change in iconification/minimization state.
02179                         if ((!event.active.gain) != mIsMinimized)
02180                         {
02181                                 mCallbacks->handleActivate(this, !!event.active.gain);
02182                                 llinfos << "SDL deiconification state switched to " << BOOL(event.active.gain) << llendl;
02183         
02184                                 mIsMinimized = (!event.active.gain);
02185                         }
02186                         else
02187                         {
02188                                 llinfos << "Ignored bogus redundant SDL deiconification state switch to " << BOOL(event.active.gain) << llendl;
02189                         }
02190                 }
02191                 break;
02192 
02193             case SDL_QUIT:
02194                             if(mCallbacks->handleCloseRequest(this))
02195                         {
02196                                 // Get the app to initiate cleanup.
02197                                 mCallbacks->handleQuit(this);
02198                                 // The app is responsible for calling destroyWindow when done with GL
02199                         }
02200                 break;
02201         default:
02202                 //llinfos << "Unhandled SDL event type " << event.type << llendl;
02203                 break;
02204         }
02205     }
02206 
02207 #if LL_X11
02208     // This is a good time to stop flashing the icon if our mFlashTimer has
02209     // expired.
02210     if (mFlashing && mFlashTimer.hasExpired())
02211     {
02212             x11_set_urgent(FALSE);
02213             mFlashing = FALSE;
02214     }
02215 #endif // LL_X11
02216 }
02217 
02218 static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty)
02219 {
02220         SDL_Cursor *sdlcursor = NULL;
02221         SDL_Surface *bmpsurface;
02222 
02223         // Load cursor pixel data from BMP file
02224         bmpsurface = Load_BMP_Resource(filename);
02225         if (bmpsurface && bmpsurface->w%8==0)
02226         {
02227                 SDL_Surface *cursurface;
02228                 lldebugs << "Loaded cursor file " << filename << " "
02229                          << bmpsurface->w << "x" << bmpsurface->h << llendl;
02230                 cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE,
02231                                                    bmpsurface->w,
02232                                                    bmpsurface->h,
02233                                                    32,
02234                                                    0xFFU,
02235                                                    0xFF00U,
02236                                                    0xFF0000U,
02237                                                    0xFF000000U);
02238                 SDL_FillRect(cursurface, NULL, 0x00000000U);
02239 
02240                 // Blit the cursor pixel data onto a 32-bit RGBA surface so we
02241                 // only have to cope with processing one type of pixel format.
02242                 if (0 == SDL_BlitSurface(bmpsurface, NULL,
02243                                          cursurface, NULL))
02244                 {
02245                         // n.b. we already checked that width is a multiple of 8.
02246                         const int bitmap_bytes = (cursurface->w * cursurface->h) / 8;
02247                         unsigned char *cursor_data = new unsigned char[bitmap_bytes];
02248                         unsigned char *cursor_mask = new unsigned char[bitmap_bytes];
02249                         memset(cursor_data, 0, bitmap_bytes);
02250                         memset(cursor_mask, 0, bitmap_bytes);
02251                         int i,j;
02252                         // Walk the RGBA cursor pixel data, extracting both data and
02253                         // mask to build SDL-friendly cursor bitmaps from.  The mask
02254                         // is inferred by color-keying against 200,200,200
02255                         for (i=0; i<cursurface->h; ++i) {
02256                                 for (j=0; j<cursurface->w; ++j) {
02257                                         unsigned char *pixelp =
02258                                                 ((unsigned char *)cursurface->pixels)
02259                                                 + cursurface->pitch * i
02260                                                 + j*cursurface->format->BytesPerPixel;
02261                                         unsigned char srcred = pixelp[0];
02262                                         unsigned char srcgreen = pixelp[1];
02263                                         unsigned char srcblue = pixelp[2];
02264                                         BOOL mask_bit = (srcred != 200)
02265                                                 || (srcgreen != 200)
02266                                                 || (srcblue != 200);
02267                                         BOOL data_bit = mask_bit && (srcgreen <= 80);//not 0x80
02268                                         unsigned char bit_offset = (cursurface->w/8) * i
02269                                                 + j/8;
02270                                         cursor_data[bit_offset] |= (data_bit) << (7 - (j&7));
02271                                         cursor_mask[bit_offset] |= (mask_bit) << (7 - (j&7));
02272                                 }
02273                         }
02274                         sdlcursor = SDL_CreateCursor((Uint8*)cursor_data,
02275                                                      (Uint8*)cursor_mask,
02276                                                      cursurface->w, cursurface->h,
02277                                                      hotx, hoty);
02278                         delete[] cursor_data;
02279                         delete[] cursor_mask;
02280                 } else {
02281                         llwarns << "CURSOR BLIT FAILURE, cursurface: " << cursurface << llendl;
02282                 }
02283                 SDL_FreeSurface(cursurface);
02284                 SDL_FreeSurface(bmpsurface);
02285         } else {
02286                 llwarns << "CURSOR LOAD FAILURE " << filename << llendl;
02287         }
02288 
02289         return sdlcursor;
02290 }
02291 
02292 void LLWindowSDL::setCursor(ECursorType cursor)
02293 {
02294         if (mCurrentCursor != cursor)
02295         {
02296                 if (cursor < UI_CURSOR_COUNT)
02297                 {
02298                         SDL_Cursor *sdlcursor = mSDLCursors[cursor];
02299                         // Try to default to the arrow for any cursors that
02300                         // did not load correctly.
02301                         if (!sdlcursor && mSDLCursors[UI_CURSOR_ARROW])
02302                                 sdlcursor = mSDLCursors[UI_CURSOR_ARROW];
02303                         if (sdlcursor)
02304                                 SDL_SetCursor(sdlcursor);
02305                 } else {
02306                         llwarns << "Tried to set invalid cursor number " << cursor << llendl;
02307                 }
02308                 mCurrentCursor = cursor;
02309         }
02310 }
02311 
02312 ECursorType LLWindowSDL::getCursor()
02313 {
02314         return mCurrentCursor;
02315 }
02316 
02317 void LLWindowSDL::initCursors()
02318 {
02319         int i;
02320         // Blank the cursor pointer array for those we may miss.
02321         for (i=0; i<UI_CURSOR_COUNT; ++i)
02322         {
02323                 mSDLCursors[i] = NULL;
02324         }
02325         // Pre-make an SDL cursor for each of the known cursor types.
02326         // We hardcode the hotspots - to avoid that we'd have to write
02327         // a .cur file loader.
02328         // NOTE: SDL doesn't load RLE-compressed BMP files.
02329         mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0);
02330         mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15);
02331         mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10);
02332         mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16);
02333         mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14);
02334         mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17);
02335         mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17);
02336         mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14);
02337         mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16);
02338         mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
02339         mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
02340         mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13);
02341         mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6);
02342         mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5);
02343         mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7);
02344         mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0);
02345         mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0);
02346         mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0);
02347         mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0);
02348         mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8);
02349         mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0);
02350         mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13);
02351         mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0);
02352         mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0);
02353         mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0);
02354         mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5);
02355         mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5);
02356         mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5);
02357         mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0);
02358         mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",0,0);
02359         mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",0,0);
02360         mSDLCursors[UI_CURSOR_TOOLPAY] = makeSDLCursorFromBMP("toolpay.BMP",0,0);
02361         mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",0,0);
02362         mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
02363 }
02364 
02365 void LLWindowSDL::quitCursors()
02366 {
02367         int i;
02368         if (mWindow)
02369         {
02370                 for (i=0; i<UI_CURSOR_COUNT; ++i)
02371                 {
02372                         if (mSDLCursors[i])
02373                         {
02374                                 SDL_FreeCursor(mSDLCursors[i]);
02375                                 mSDLCursors[i] = NULL;
02376                         }
02377                 }
02378         } else {
02379                 // SDL doesn't refcount cursors, so if the window has
02380                 // already been destroyed then the cursors have gone with it.
02381                 llinfos << "Skipping quitCursors: mWindow already gone." << llendl;
02382                 for (i=0; i<UI_CURSOR_COUNT; ++i)
02383                         mSDLCursors[i] = NULL;
02384         }
02385 }
02386 
02387 void LLWindowSDL::captureMouse()
02388 {
02389         // SDL already enforces the semantics that captureMouse is
02390         // used for, i.e. that we continue to get mouse events as long
02391         // as a button is down regardless of whether we left the
02392         // window, and in a less obnoxious way than SDL_WM_GrabInput
02393         // which would confine the cursor to the window too.
02394 
02395         //llinfos << "LLWindowSDL::captureMouse" << llendl;
02396 }
02397 
02398 void LLWindowSDL::releaseMouse()
02399 {
02400         // see LWindowSDL::captureMouse()
02401         
02402         //llinfos << "LLWindowSDL::releaseMouse" << llendl;
02403 }
02404 
02405 void LLWindowSDL::hideCursor()
02406 {
02407         if(!mCursorHidden)
02408         {
02409                 // llinfos << "hideCursor: hiding" << llendl;
02410                 mCursorHidden = TRUE;
02411                 mHideCursorPermanent = TRUE;
02412                 SDL_ShowCursor(0);
02413         }
02414         else
02415         {
02416                 // llinfos << "hideCursor: already hidden" << llendl;
02417         }
02418 
02419         adjustCursorDecouple();
02420 }
02421 
02422 void LLWindowSDL::showCursor()
02423 {
02424         if(mCursorHidden)
02425         {
02426                 // llinfos << "showCursor: showing" << llendl;
02427                 mCursorHidden = FALSE;
02428                 mHideCursorPermanent = FALSE;
02429                 SDL_ShowCursor(1);
02430         }
02431         else
02432         {
02433                 // llinfos << "showCursor: already visible" << llendl;
02434         }
02435 
02436         adjustCursorDecouple();
02437 }
02438 
02439 void LLWindowSDL::showCursorFromMouseMove()
02440 {
02441         if (!mHideCursorPermanent)
02442         {
02443                 showCursor();
02444         }
02445 }
02446 
02447 void LLWindowSDL::hideCursorUntilMouseMove()
02448 {
02449         if (!mHideCursorPermanent)
02450         {
02451                 hideCursor();
02452                 mHideCursorPermanent = FALSE;
02453         }
02454 }
02455 
02456 
02457 
02458 //
02459 // LLSplashScreenSDL - I don't think we'll bother to implement this; it's
02460 // fairly obsolete at this point.
02461 //
02462 LLSplashScreenSDL::LLSplashScreenSDL()
02463 {
02464 }
02465 
02466 LLSplashScreenSDL::~LLSplashScreenSDL()
02467 {
02468 }
02469 
02470 void LLSplashScreenSDL::showImpl()
02471 {
02472 }
02473 
02474 void LLSplashScreenSDL::updateImpl(const char* mesg)
02475 {
02476 }
02477 
02478 void LLSplashScreenSDL::hideImpl()
02479 {
02480 }
02481 
02482 
02483 
02484 #if LL_GTK
02485 static void response_callback (GtkDialog *dialog,
02486                                gint       arg1,
02487                                gpointer   user_data)
02488 {
02489         gint *response = (gint*)user_data;
02490         *response = arg1;
02491         gtk_widget_destroy(GTK_WIDGET(dialog));
02492         gtk_main_quit();
02493 }
02494 
02495 S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
02496 {
02497         S32 rtn = OSBTN_CANCEL;
02498 
02499         ll_try_gtk_init();
02500 
02501         if(gWindowImplementation != NULL)
02502                 gWindowImplementation->beforeDialog();
02503 
02504         if (ll_try_gtk_init()
02505             // We can NOT expect to combine GTK and SDL's aggressive fullscreen
02506             && ((NULL==gWindowImplementation) || (!was_fullscreen))
02507             )
02508         {
02509                 GtkWidget *win = NULL;
02510 
02511                 llinfos << "Creating a dialog because we're in windowed mode and GTK is happy." << llendl;
02512                 
02513                 GtkDialogFlags flags = GTK_DIALOG_MODAL;
02514                 GtkMessageType messagetype;
02515                 GtkButtonsType buttons;
02516                 switch (type)
02517                 {
02518                 default:
02519                 case OSMB_OK:
02520                         messagetype = GTK_MESSAGE_WARNING;
02521                         buttons = GTK_BUTTONS_OK;
02522                         break;
02523                 case OSMB_OKCANCEL:
02524                         messagetype = GTK_MESSAGE_QUESTION;
02525                         buttons = GTK_BUTTONS_OK_CANCEL;
02526                         break;
02527                 case OSMB_YESNO:
02528                         messagetype = GTK_MESSAGE_QUESTION;
02529                         buttons = GTK_BUTTONS_YES_NO;
02530                         break;
02531                 }
02532                 win = gtk_message_dialog_new(NULL,
02533                                              flags, messagetype, buttons,
02534                                              text);
02535 
02536 # if LL_X11
02537                 // Make GTK tell the window manager to associate this
02538                 // dialog with our non-GTK SDL window, which should try
02539                 // to keep it on top etc.
02540                 if (gWindowImplementation &&
02541                     gWindowImplementation->mSDL_XWindowID != None)
02542                 {
02543                         gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
02544                         GdkWindow *gdkwin = gdk_window_foreign_new(gWindowImplementation->mSDL_XWindowID);
02545                         gdk_window_set_transient_for(GTK_WIDGET(win)->window,
02546                                                      gdkwin);
02547                 }
02548 # endif //LL_X11
02549 
02550                 gtk_window_set_position(GTK_WINDOW(win),
02551                                         GTK_WIN_POS_CENTER_ON_PARENT);
02552 
02553                 gtk_window_set_type_hint(GTK_WINDOW(win),
02554                                          GDK_WINDOW_TYPE_HINT_DIALOG);
02555 
02556                 if (caption)
02557                         gtk_window_set_title(GTK_WINDOW(win), caption);
02558 
02559                 gint response = GTK_RESPONSE_NONE;
02560                 g_signal_connect (win,
02561                                   "response", 
02562                                   G_CALLBACK (response_callback),
02563                                   &response);
02564 
02565                 // we should be able to use a gtk_dialog_run(), but it's
02566                 // apparently not written to exist in a world without a higher
02567                 // gtk_main(), so we manage its signal/destruction outselves.
02568                 gtk_widget_show_all (win);
02569                 gtk_main();
02570 
02571                 //llinfos << "response: " << response << llendl;
02572                 switch (response)
02573                 {
02574                 case GTK_RESPONSE_OK:     rtn = OSBTN_OK; break;
02575                 case GTK_RESPONSE_YES:    rtn = OSBTN_YES; break;
02576                 case GTK_RESPONSE_NO:     rtn = OSBTN_NO; break;
02577                 case GTK_RESPONSE_APPLY:  rtn = OSBTN_OK; break;
02578                 case GTK_RESPONSE_NONE:
02579                 case GTK_RESPONSE_CANCEL:
02580                 case GTK_RESPONSE_CLOSE:
02581                 case GTK_RESPONSE_DELETE_EVENT:
02582                 default: rtn = OSBTN_CANCEL;
02583                 }
02584         }
02585         else
02586         {
02587                 llinfos << "MSGBOX: " << caption << ": " << text << llendl;
02588                 llinfos << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << llendl;
02589                 rtn = OSBTN_OK;
02590         }
02591 
02592         if(gWindowImplementation != NULL)
02593                 gWindowImplementation->afterDialog();
02594 
02595         return rtn;
02596 }
02597 
02598 static void color_changed_callback(GtkWidget *widget,
02599                                    gpointer user_data)
02600 {
02601         GtkColorSelection *colorsel = GTK_COLOR_SELECTION(widget);
02602         GdkColor *colorp = (GdkColor*)user_data;
02603         
02604         gtk_color_selection_get_current_color(colorsel, colorp);
02605 }
02606 
02607 BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
02608 {
02609         BOOL rtn = FALSE;
02610 
02611         beforeDialog();
02612 
02613         if (ll_try_gtk_init()
02614             // We can NOT expect to combine GTK and SDL's aggressive fullscreen
02615             && !was_fullscreen
02616             )
02617         {
02618                 GtkWidget *win = NULL;
02619 
02620                 win = gtk_color_selection_dialog_new(NULL);
02621 
02622 # if LL_X11
02623                 // Get GTK to tell the window manager to associate this
02624                 // dialog with our non-GTK SDL window, which should try
02625                 // to keep it on top etc.
02626                 if (mSDL_XWindowID != None)
02627                 {
02628                         gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
02629                         GdkWindow *gdkwin = gdk_window_foreign_new(mSDL_XWindowID);
02630                         gdk_window_set_transient_for(GTK_WIDGET(win)->window,
02631                                                      gdkwin);
02632                 }
02633 # endif //LL_X11
02634 
02635                 GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel);
02636 
02637                 GdkColor color, orig_color;
02638                 orig_color.red = guint16(65535 * *r);
02639                 orig_color.green= guint16(65535 * *g);
02640                 orig_color.blue = guint16(65535 * *b);
02641                 color = orig_color;
02642 
02643                 gtk_color_selection_set_previous_color (colorsel, &color);
02644                 gtk_color_selection_set_current_color (colorsel, &color);
02645                 gtk_color_selection_set_has_palette (colorsel, TRUE);
02646                 gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
02647 
02648                 gint response = GTK_RESPONSE_NONE;
02649                 g_signal_connect (win,
02650                                   "response", 
02651                                   G_CALLBACK (response_callback),
02652                                   &response);
02653 
02654                 g_signal_connect (G_OBJECT (colorsel), "color_changed",
02655                                   G_CALLBACK (color_changed_callback),
02656                                   &color);
02657 
02658                 gtk_window_set_modal(GTK_WINDOW(win), TRUE);
02659                 gtk_widget_show_all(win);
02660                 // hide the help button - we don't service it.
02661                 gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button);
02662                 gtk_main();
02663 
02664                 if (response == GTK_RESPONSE_OK &&
02665                     (orig_color.red != color.red
02666                      || orig_color.green != color.green
02667                      || orig_color.blue != color.blue) )
02668                 {
02669                         *r = color.red / 65535.0f;
02670                         *g = color.green / 65535.0f;
02671                         *b = color.blue / 65535.0f;
02672                         rtn = TRUE;
02673                 }
02674         }
02675 
02676         afterDialog();
02677 
02678         return rtn;
02679 }
02680 #else
02681 S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
02682 {
02683         llinfos << "MSGBOX: " << caption << ": " << text << llendl;
02684         return 0;
02685 }
02686 
02687 BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
02688 {
02689         return (FALSE);
02690 }
02691 #endif // LL_GTK
02692 
02693 // Open a URL with the user's default web browser.
02694 // Must begin with protocol identifier.
02695 void spawn_web_browser(const char* escaped_url)
02696 {
02697         llinfos << "spawn_web_browser: " << escaped_url << llendl;
02698         
02699 #if LL_LINUX || LL_SOLARIS
02700 # if LL_X11
02701         if (gWindowImplementation && gWindowImplementation->mSDL_Display)
02702         {
02703                 maybe_lock_display();
02704                 // Just in case - before forking.
02705                 XSync(gWindowImplementation->mSDL_Display, False);
02706                 maybe_unlock_display();
02707         }
02708 # endif // LL_X11
02709 
02710         std::string cmd;
02711         cmd  = gDirUtilp->getAppRODataDir().c_str();
02712         cmd += gDirUtilp->getDirDelimiter().c_str();
02713         cmd += "launch_url.sh";
02714         char* const argv[] = {(char*)cmd.c_str(), (char*)escaped_url, NULL};
02715 
02716         fflush(NULL);
02717         pid_t pid = fork();
02718         if (pid == 0)
02719         { // child
02720                 // disconnect from stdin/stdout/stderr, or child will
02721                 // keep our output pipe undesirably alive if it outlives us.
02722                 close(0);
02723                 close(1);
02724                 close(2);
02725                 // end ourself by running the command
02726                 execv(cmd.c_str(), argv);       /* Flawfinder: ignore */
02727                 // if execv returns at all, there was a problem.
02728                 llwarns << "execv failure when trying to start " << cmd << llendl;
02729                 _exit(1); // _exit because we don't want atexit() clean-up!
02730         } else {
02731                 if (pid > 0)
02732                 {
02733                         // parent - wait for child to die
02734                         int childExitStatus;
02735                         waitpid(pid, &childExitStatus, 0);
02736                 } else {
02737                         llwarns << "fork failure." << llendl;
02738                 }
02739         }
02740 #endif // LL_LINUX || LL_SOLARIS
02741 
02742         llinfos << "spawn_web_browser returning." << llendl;
02743 }
02744 
02745 void shell_open( const char* file_path )
02746 {
02747         // *TODO: This function is deprecated and should probably go away.
02748         llwarns << "Deprecated shell_open(): " << file_path << llendl;
02749 }
02750 
02751 void *LLWindowSDL::getPlatformWindow()
02752 {
02753 #if LL_GTK && LL_LIBXUL_ENABLED
02754         if (ll_try_gtk_init())
02755         {
02756                 maybe_lock_display();
02757                 GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
02758 
02759                 // show the hidden-widget while debugging (needs mozlib change)
02760                 //gtk_widget_show_all(GTK_WIDGET(win));
02761 
02762                 gtk_widget_realize(GTK_WIDGET(win));
02763                 maybe_unlock_display();
02764                 return win;
02765         }
02766 #endif // LL_GTK && LL_LIBXUL_ENABLED
02767         // Unixoid mozilla really needs GTK.
02768         return NULL;
02769 }
02770 
02771 void LLWindowSDL::bringToFront()
02772 {
02773         // This is currently used when we are 'launched' to a specific
02774         // map position externally.
02775         llinfos << "bringToFront" << llendl;
02776 #if LL_X11
02777         if (mSDL_Display && !mFullscreen)
02778         {
02779                 maybe_lock_display();
02780                 XRaiseWindow(mSDL_Display, mSDL_XWindowID);
02781                 XSync(mSDL_Display, False);
02782                 maybe_unlock_display();
02783         }
02784 #endif // LL_X11
02785 }
02786 
02787 #endif // LL_SDL

Generated on Thu Jul 1 06:09:46 2010 for Second Life Viewer by  doxygen 1.4.7