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

Generated on Fri May 16 08:33:04 2008 for SecondLife by  doxygen 1.5.5