PageRenderTime 179ms CodeModel.GetById 31ms app.highlight 133ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llwindow/llwindowsdl.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2389 lines | 1738 code | 343 blank | 308 comment | 282 complexity | e27d5b09f5e8acc10cb18969d3425392 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llwindowsdl.cpp
   3 * @brief SDL implementation of LLWindow class
   4 * @author This module has many fathers, and it shows.
   5 *
   6 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2010, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 */
  27
  28#if LL_SDL
  29
  30#include "linden_common.h"
  31
  32#include "llwindowsdl.h"
  33
  34#include "llwindowcallbacks.h"
  35#include "llkeyboardsdl.h"
  36
  37#include "llerror.h"
  38#include "llgl.h"
  39#include "llstring.h"
  40#include "lldir.h"
  41#include "llfindlocale.h"
  42
  43#if LL_GTK
  44extern "C" {
  45# include "gtk/gtk.h"
  46}
  47#include <locale.h>
  48#endif // LL_GTK
  49
  50extern "C" {
  51# include "fontconfig/fontconfig.h"
  52}
  53
  54#if LL_LINUX || LL_SOLARIS
  55// not necessarily available on random SDL platforms, so #if LL_LINUX
  56// for execv(), waitpid(), fork()
  57# include <unistd.h>
  58# include <sys/types.h>
  59# include <sys/wait.h>
  60#endif // LL_LINUX || LL_SOLARIS
  61
  62extern BOOL gDebugWindowProc;
  63
  64const S32 MAX_NUM_RESOLUTIONS = 200;
  65
  66// static variable for ATI mouse cursor crash work-around:
  67static bool ATIbug = false; 
  68
  69//
  70// LLWindowSDL
  71//
  72
  73#if LL_X11
  74# include <X11/Xutil.h>
  75#endif //LL_X11
  76
  77// TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for a similar
  78// set of reasons): Stash a pointer to the LLWindowSDL object here and
  79// maintain in the constructor and destructor.  This assumes that there will
  80// be only one object of this class at any time.  Currently this is true.
  81static LLWindowSDL *gWindowImplementation = NULL;
  82
  83
  84void maybe_lock_display(void)
  85{
  86	if (gWindowImplementation && gWindowImplementation->Lock_Display) {
  87		gWindowImplementation->Lock_Display();
  88	}
  89}
  90
  91
  92void maybe_unlock_display(void)
  93{
  94	if (gWindowImplementation && gWindowImplementation->Unlock_Display) {
  95		gWindowImplementation->Unlock_Display();
  96	}
  97}
  98
  99
 100#if LL_GTK
 101// Lazily initialize and check the runtime GTK version for goodness.
 102// static
 103bool LLWindowSDL::ll_try_gtk_init(void)
 104{
 105	static BOOL done_gtk_diag = FALSE;
 106	static BOOL gtk_is_good = FALSE;
 107	static BOOL done_setlocale = FALSE;
 108	static BOOL tried_gtk_init = FALSE;
 109
 110	if (!done_setlocale)
 111	{
 112		llinfos << "Starting GTK Initialization." << llendl;
 113		maybe_lock_display();
 114		gtk_disable_setlocale();
 115		maybe_unlock_display();
 116		done_setlocale = TRUE;
 117	}
 118	
 119	if (!tried_gtk_init)
 120	{
 121		tried_gtk_init = TRUE;
 122		if (!g_thread_supported ()) g_thread_init (NULL);
 123		maybe_lock_display();
 124		gtk_is_good = gtk_init_check(NULL, NULL);
 125		maybe_unlock_display();
 126		if (!gtk_is_good)
 127			llwarns << "GTK Initialization failed." << llendl;
 128	}
 129
 130	if (gtk_is_good && !done_gtk_diag)
 131	{
 132		llinfos << "GTK Initialized." << llendl;
 133		llinfos << "- Compiled against GTK version "
 134			<< GTK_MAJOR_VERSION << "."
 135			<< GTK_MINOR_VERSION << "."
 136			<< GTK_MICRO_VERSION << llendl;
 137		llinfos << "- Running against GTK version "
 138			<< gtk_major_version << "."
 139			<< gtk_minor_version << "."
 140			<< gtk_micro_version << llendl;
 141		maybe_lock_display();
 142		const gchar* gtk_warning = gtk_check_version(
 143			GTK_MAJOR_VERSION,
 144			GTK_MINOR_VERSION,
 145			GTK_MICRO_VERSION);
 146		maybe_unlock_display();
 147		if (gtk_warning)
 148		{
 149			llwarns << "- GTK COMPATIBILITY WARNING: " <<
 150				gtk_warning << llendl;
 151			gtk_is_good = FALSE;
 152		} else {
 153			llinfos << "- GTK version is good." << llendl;
 154		}
 155
 156		done_gtk_diag = TRUE;
 157	}
 158
 159	return gtk_is_good;
 160}
 161#endif // LL_GTK
 162
 163
 164#if LL_X11
 165// static
 166Window LLWindowSDL::get_SDL_XWindowID(void)
 167{
 168	if (gWindowImplementation) {
 169		return gWindowImplementation->mSDL_XWindowID;
 170	}
 171	return None;
 172}
 173
 174//static
 175Display* LLWindowSDL::get_SDL_Display(void)
 176{
 177	if (gWindowImplementation) {
 178		return gWindowImplementation->mSDL_Display;
 179	}
 180	return NULL;
 181}
 182#endif // LL_X11
 183
 184
 185LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks,
 186			 const std::string& title, S32 x, S32 y, S32 width,
 187			 S32 height, U32 flags,
 188			 BOOL fullscreen, BOOL clearBg,
 189			 BOOL disable_vsync, BOOL use_gl,
 190			 BOOL ignore_pixel_depth, U32 fsaa_samples)
 191	: LLWindow(callbacks, fullscreen, flags),
 192	  Lock_Display(NULL),
 193	  Unlock_Display(NULL), mGamma(1.0f)
 194{
 195	// Initialize the keyboard
 196	gKeyboard = new LLKeyboardSDL();
 197	gKeyboard->setCallbacks(callbacks);
 198	// Note that we can't set up key-repeat until after SDL has init'd video
 199
 200	// Ignore use_gl for now, only used for drones on PC
 201	mWindow = NULL;
 202	mNeedsResize = FALSE;
 203	mOverrideAspectRatio = 0.f;
 204	mGrabbyKeyFlags = 0;
 205	mReallyCapturedCount = 0;
 206	mHaveInputFocus = -1;
 207	mIsMinimized = -1;
 208	mFSAASamples = fsaa_samples;
 209
 210#if LL_X11
 211	mSDL_XWindowID = None;
 212	mSDL_Display = NULL;
 213#endif // LL_X11
 214
 215#if LL_GTK
 216	// We MUST be the first to initialize GTK so that GTK doesn't get badly
 217	// initialized with a non-C locale and cause lots of serious random
 218	// weirdness.
 219	ll_try_gtk_init();
 220#endif // LL_GTK
 221
 222	// Assume 4:3 aspect ratio until we know better
 223	mOriginalAspectRatio = 1024.0 / 768.0;
 224
 225	if (title.empty())
 226		mWindowTitle = "SDL Window";  // *FIX: (???)
 227	else
 228		mWindowTitle = title;
 229
 230	// Create the GL context and set it up for windowed or fullscreen, as appropriate.
 231	if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
 232	{
 233		gGLManager.initGL();
 234
 235		//start with arrow cursor
 236		initCursors();
 237		setCursor( UI_CURSOR_ARROW );
 238	}
 239
 240	stop_glerror();
 241
 242	// Stash an object pointer for OSMessageBox()
 243	gWindowImplementation = this;
 244
 245#if LL_X11
 246	mFlashing = FALSE;
 247#endif // LL_X11
 248
 249	mKeyScanCode = 0;
 250	mKeyVirtualKey = 0;
 251	mKeyModifiers = KMOD_NONE;
 252}
 253
 254static SDL_Surface *Load_BMP_Resource(const char *basename)
 255{
 256	const int PATH_BUFFER_SIZE=1000;
 257	char path_buffer[PATH_BUFFER_SIZE];	/* Flawfinder: ignore */
 258	
 259	// Figure out where our BMP is living on the disk
 260	snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",	
 261		 gDirUtilp->getAppRODataDir().c_str(),
 262		 gDirUtilp->getDirDelimiter().c_str(),
 263		 gDirUtilp->getDirDelimiter().c_str(),
 264		 basename);
 265	path_buffer[PATH_BUFFER_SIZE-1] = '\0';
 266	
 267	return SDL_LoadBMP(path_buffer);
 268}
 269
 270#if LL_X11
 271// This is an XFree86/XOrg-specific hack for detecting the amount of Video RAM
 272// on this machine.  It works by searching /var/log/var/log/Xorg.?.log or
 273// /var/log/XFree86.?.log for a ': (VideoRAM ?|Memory): (%d+) kB' regex, where
 274// '?' is the X11 display number derived from $DISPLAY
 275static int x11_detect_VRAM_kb_fp(FILE *fp, const char *prefix_str)
 276{
 277	const int line_buf_size = 1000;
 278	char line_buf[line_buf_size];
 279	while (fgets(line_buf, line_buf_size, fp))
 280	{
 281		//lldebugs << "XLOG: " << line_buf << llendl;
 282
 283		// Why the ad-hoc parser instead of using a regex?  Our
 284		// favourite regex implementation - libboost_regex - is
 285		// quite a heavy and troublesome dependency for the client, so
 286		// it seems a shame to introduce it for such a simple task.
 287		// *FIXME: libboost_regex is a dependency now anyway, so we may
 288		// as well use it instead of this hand-rolled nonsense.
 289		const char *part1_template = prefix_str;
 290		const char part2_template[] = " kB";
 291		char *part1 = strstr(line_buf, part1_template);
 292		if (part1) // found start of matching line
 293		{
 294			part1 = &part1[strlen(part1_template)]; // -> after
 295			char *part2 = strstr(part1, part2_template);
 296			if (part2) // found end of matching line
 297			{
 298				// now everything between part1 and part2 is
 299				// supposed to be numeric, describing the
 300				// number of kB of Video RAM supported
 301				int rtn = 0;
 302				for (; part1 < part2; ++part1)
 303				{
 304					if (*part1 < '0' || *part1 > '9')
 305					{
 306						// unexpected char, abort parse
 307						rtn = 0;
 308						break;
 309					}
 310					rtn *= 10;
 311					rtn += (*part1) - '0';
 312				}
 313				if (rtn > 0)
 314				{
 315					// got the kB number.  return it now.
 316					return rtn;
 317				}
 318			}
 319		}
 320	}
 321	return 0; // 'could not detect'
 322}
 323
 324static int x11_detect_VRAM_kb()
 325{
 326#if LL_SOLARIS && defined(__sparc)
 327      //  NOTE: there's no Xorg server on SPARC so just return 0
 328      //        and allow SDL to attempt to get the amount of VRAM
 329      return(0);
 330#else
 331
 332	std::string x_log_location("/var/log/");
 333	std::string fname;
 334	int rtn = 0; // 'could not detect'
 335	int display_num = 0;
 336	FILE *fp;
 337	char *display_env = getenv("DISPLAY"); // e.g. :0 or :0.0 or :1.0 etc
 338	// parse DISPLAY number so we can go grab the right log file
 339	if (display_env[0] == ':' &&
 340	    display_env[1] >= '0' && display_env[1] <= '9')
 341	{
 342		display_num = display_env[1] - '0';
 343	}
 344
 345	// *TODO: we could be smarter and see which of Xorg/XFree86 has the
 346	// freshest time-stamp.
 347
 348	// Try Xorg log first
 349	fname = x_log_location;
 350	fname += "Xorg.";
 351	fname += ('0' + display_num);
 352	fname += ".log";
 353	fp = fopen(fname.c_str(), "r");
 354	if (fp)
 355	{
 356		llinfos << "Looking in " << fname
 357			<< " for VRAM info..." << llendl;
 358		rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
 359		fclose(fp);
 360		if (0 == rtn)
 361		{
 362			fp = fopen(fname.c_str(), "r");
 363			if (fp)
 364			{
 365				rtn = x11_detect_VRAM_kb_fp(fp, ": Video RAM: ");
 366				fclose(fp);
 367				if (0 == rtn)
 368				{
 369					fp = fopen(fname.c_str(), "r");
 370					if (fp)
 371					{
 372						rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
 373						fclose(fp);
 374					}
 375				}
 376			}
 377		}
 378	}
 379	else
 380	{
 381		llinfos << "Could not open " << fname
 382			<< " - skipped." << llendl;	
 383		// Try old XFree86 log otherwise
 384		fname = x_log_location;
 385		fname += "XFree86.";
 386		fname += ('0' + display_num);
 387		fname += ".log";
 388		fp = fopen(fname.c_str(), "r");
 389		if (fp)
 390		{
 391			llinfos << "Looking in " << fname
 392				<< " for VRAM info..." << llendl;
 393			rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
 394			fclose(fp);
 395			if (0 == rtn)
 396			{
 397				fp = fopen(fname.c_str(), "r");
 398				if (fp)
 399				{
 400					rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
 401					fclose(fp);
 402				}
 403			}
 404		}
 405		else
 406		{
 407			llinfos << "Could not open " << fname
 408				<< " - skipped." << llendl;
 409		}
 410	}
 411	return rtn;
 412#endif // LL_SOLARIS
 413}
 414#endif // LL_X11
 415
 416BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
 417{
 418	//bool			glneedsinit = false;
 419
 420	llinfos << "createContext, fullscreen=" << fullscreen <<
 421	    " size=" << width << "x" << height << llendl;
 422
 423	// captures don't survive contexts
 424	mGrabbyKeyFlags = 0;
 425	mReallyCapturedCount = 0;
 426	
 427	if (SDL_Init(SDL_INIT_VIDEO) < 0)
 428	{
 429		llinfos << "sdl_init() failed! " << SDL_GetError() << llendl;
 430		setupFailure("sdl_init() failure,  window creation error", "error", OSMB_OK);
 431		return false;
 432	}
 433
 434	SDL_version c_sdl_version;
 435	SDL_VERSION(&c_sdl_version);
 436	llinfos << "Compiled against SDL "
 437		<< int(c_sdl_version.major) << "."
 438		<< int(c_sdl_version.minor) << "."
 439		<< int(c_sdl_version.patch) << llendl;
 440	const SDL_version *r_sdl_version;
 441	r_sdl_version = SDL_Linked_Version();
 442	llinfos << " Running against SDL "
 443		<< int(r_sdl_version->major) << "."
 444		<< int(r_sdl_version->minor) << "."
 445		<< int(r_sdl_version->patch) << llendl;
 446
 447	const SDL_VideoInfo *video_info = SDL_GetVideoInfo( );
 448	if (!video_info)
 449	{
 450		llinfos << "SDL_GetVideoInfo() failed! " << SDL_GetError() << llendl;
 451		setupFailure("SDL_GetVideoInfo() failed, Window creation error", "Error", OSMB_OK);
 452		return FALSE;
 453	}
 454
 455	if (video_info->current_h > 0)
 456	{
 457		mOriginalAspectRatio = (float)video_info->current_w / (float)video_info->current_h;
 458		llinfos << "Original aspect ratio was " << video_info->current_w << ":" << video_info->current_h << "=" << mOriginalAspectRatio << llendl;
 459	}
 460
 461	SDL_EnableUNICODE(1);
 462	SDL_WM_SetCaption(mWindowTitle.c_str(), mWindowTitle.c_str());
 463
 464	// Set the application icon.
 465	SDL_Surface *bmpsurface;
 466	bmpsurface = Load_BMP_Resource("ll_icon.BMP");
 467	if (bmpsurface)
 468	{
 469		// This attempts to give a black-keyed mask to the icon.
 470		SDL_SetColorKey(bmpsurface,
 471				SDL_SRCCOLORKEY,
 472				SDL_MapRGB(bmpsurface->format, 0,0,0) );
 473		SDL_WM_SetIcon(bmpsurface, NULL);
 474		// The SDL examples cheerfully avoid freeing the icon
 475		// surface, but I'm betting that's leaky.
 476		SDL_FreeSurface(bmpsurface);
 477		bmpsurface = NULL;
 478	}
 479
 480	// note: these SetAttributes make Tom's 9600-on-AMD64 fail to
 481	// get a visual, but it's broken anyway when it does, and without
 482	// these SetAttributes we might easily get an avoidable substandard
 483	// visual to work with on most other machines.
 484	SDL_GL_SetAttribute(SDL_GL_RED_SIZE,  8);
 485	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
 486	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
 487#if !LL_SOLARIS
 488        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24);
 489	// We need stencil support for a few (minor) things.
 490	if (!getenv("LL_GL_NO_STENCIL"))
 491		SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
 492#else
 493	// NOTE- use smaller Z-buffer to enable more graphics cards
 494        //     - This should not affect better GPUs and has been proven
 495        //	 to provide 24-bit z-buffers when available.
 496	//
 497        // As the API states: 
 498	//
 499        // GLX_DEPTH_SIZE    Must be followed by a nonnegative
 500        //                   minimum size specification.  If this
 501        //                   value is zero, visuals with no depth
 502        //                   buffer are preferred.  Otherwise, the
 503        //                   largest available depth buffer of at
 504        //                   least the minimum size is preferred.
 505
 506        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
 507#endif
 508        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
 509
 510        // *FIX: try to toggle vsync here?
 511
 512	mFullscreen = fullscreen;
 513
 514	int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
 515
 516	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 517
 518	if (mFSAASamples > 0)
 519	{
 520		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
 521		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, mFSAASamples);
 522	}
 523	
 524    	mSDLFlags = sdlflags;
 525
 526	if (mFullscreen)
 527	{
 528		llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
 529
 530		// If the requested width or height is 0, find the best default for the monitor.
 531		if((width == 0) || (height == 0))
 532		{
 533			// Scan through the list of modes, looking for one which has:
 534			//		height between 700 and 800
 535			//		aspect ratio closest to the user's original mode
 536			S32 resolutionCount = 0;
 537			LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
 538
 539			if(resolutionList != NULL)
 540			{
 541				F32 closestAspect = 0;
 542				U32 closestHeight = 0;
 543				U32 closestWidth = 0;
 544				int i;
 545
 546				llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
 547
 548				for(i=0; i < resolutionCount; i++)
 549				{
 550					F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
 551
 552					llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
 553
 554					if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
 555						(fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
 556					{
 557						llinfos << " (new closest mode) " << llendl;
 558
 559						// This is the closest mode we've seen yet.
 560						closestWidth = resolutionList[i].mWidth;
 561						closestHeight = resolutionList[i].mHeight;
 562						closestAspect = aspect;
 563					}
 564				}
 565
 566				width = closestWidth;
 567				height = closestHeight;
 568			}
 569		}
 570
 571		if((width == 0) || (height == 0))
 572		{
 573			// Mode search failed for some reason.  Use the old-school default.
 574			width = 1024;
 575			height = 768;
 576		}
 577
 578		mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
 579		if (!mWindow && bits > 16)
 580		{
 581			SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
 582			mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
 583		}
 584
 585		if (mWindow)
 586		{
 587			mFullscreen = TRUE;
 588			mFullscreenWidth   = mWindow->w;
 589			mFullscreenHeight  = mWindow->h;
 590			mFullscreenBits    = mWindow->format->BitsPerPixel;
 591			mFullscreenRefresh = -1;
 592
 593			llinfos << "Running at " << mFullscreenWidth
 594				<< "x"   << mFullscreenHeight
 595				<< "x"   << mFullscreenBits
 596				<< " @ " << mFullscreenRefresh
 597				<< llendl;
 598		}
 599		else
 600		{
 601			llwarns << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << llendl;
 602			// No fullscreen support
 603			mFullscreen = FALSE;
 604			mFullscreenWidth   = -1;
 605			mFullscreenHeight  = -1;
 606			mFullscreenBits    = -1;
 607			mFullscreenRefresh = -1;
 608
 609			std::string error = llformat("Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);	
 610			OSMessageBox(error, "Error", OSMB_OK);
 611		}
 612	}
 613
 614	if(!mFullscreen && (mWindow == NULL))
 615	{
 616		if (width == 0)
 617		    width = 1024;
 618		if (height == 0)
 619		    width = 768;
 620
 621		llinfos << "createContext: creating window " << width << "x" << height << "x" << bits << llendl;
 622		mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
 623		if (!mWindow && bits > 16)
 624		{
 625			SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
 626			mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
 627		}
 628
 629		if (!mWindow)
 630		{
 631			llwarns << "createContext: window creation failure. SDL: " << SDL_GetError() << llendl;
 632			setupFailure("Window creation error", "Error", OSMB_OK);
 633			return FALSE;
 634		}
 635	} else if (!mFullscreen && (mWindow != NULL))
 636	{
 637		llinfos << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << llendl;
 638	}
 639	
 640	// Detect video memory size.
 641# if LL_X11
 642	gGLManager.mVRAM = x11_detect_VRAM_kb() / 1024;
 643	if (gGLManager.mVRAM != 0)
 644	{
 645		llinfos << "X11 log-parser detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
 646	} else
 647# endif // LL_X11
 648	{
 649		// fallback to letting SDL detect VRAM.
 650		// note: I've not seen SDL's detection ever actually find
 651		// VRAM != 0, but if SDL *does* detect it then that's a bonus.
 652		gGLManager.mVRAM = video_info->video_mem / 1024;
 653		if (gGLManager.mVRAM != 0)
 654		{
 655			llinfos << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
 656		}
 657	}
 658	// If VRAM is not detected, that is handled later
 659
 660	// *TODO: Now would be an appropriate time to check for some
 661	// explicitly unsupported cards.
 662	//const char* RENDERER = (const char*) glGetString(GL_RENDERER);
 663
 664	GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits;
 665
 666	glGetIntegerv(GL_RED_BITS, &redBits);
 667	glGetIntegerv(GL_GREEN_BITS, &greenBits);
 668	glGetIntegerv(GL_BLUE_BITS, &blueBits);
 669	glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
 670	glGetIntegerv(GL_DEPTH_BITS, &depthBits);
 671	glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
 672	
 673	llinfos << "GL buffer:" << llendl;
 674        llinfos << "  Red Bits " << S32(redBits) << llendl;
 675        llinfos << "  Green Bits " << S32(greenBits) << llendl;
 676        llinfos << "  Blue Bits " << S32(blueBits) << llendl;
 677	llinfos	<< "  Alpha Bits " << S32(alphaBits) << llendl;
 678	llinfos	<< "  Depth Bits " << S32(depthBits) << llendl;
 679	llinfos	<< "  Stencil Bits " << S32(stencilBits) << llendl;
 680
 681	GLint colorBits = redBits + greenBits + blueBits + alphaBits;
 682	// fixme: actually, it's REALLY important for picking that we get at
 683	// least 8 bits each of red,green,blue.  Alpha we can be a bit more
 684	// relaxed about if we have to.
 685#if LL_SOLARIS && defined(__sparc)
 686//  again the __sparc required because Xsun support, 32bit are very pricey on SPARC
 687	if(colorBits < 24)		//HACK:  on SPARC allow 24-bit color
 688#else
 689	if (colorBits < 32)
 690#endif
 691	{
 692		close();
 693		setupFailure(
 694#if LL_SOLARIS && defined(__sparc)
 695			"Second Life requires at least 24-bit color on SPARC to run in a window.\n"
 696			"Please use fbconfig to set your default color depth to 24 bits.\n"
 697			"You may also need to adjust the X11 setting in SMF.  To do so use\n"
 698			"  'svccfg -s svc:/application/x11/x11-server setprop options/default_depth=24'\n"
 699#else
 700			"Second Life requires True Color (32-bit) to run in a window.\n"
 701			"Please go to Control Panels -> Display -> Settings and\n"
 702			"set the screen to 32-bit color.\n"
 703#endif
 704			"Alternately, if you choose to run fullscreen, Second Life\n"
 705			"will automatically adjust the screen each time it runs.",
 706			"Error",
 707			OSMB_OK);
 708		return FALSE;
 709	}
 710
 711#if 0  // *FIX: we're going to brave it for now...
 712	if (alphaBits < 8)
 713	{
 714		close();
 715		setupFailure(
 716			"Second Life is unable to run because it can't get an 8 bit alpha\n"
 717			"channel.  Usually this is due to video card driver issues.\n"
 718			"Please make sure you have the latest video card drivers installed.\n"
 719			"Also be sure your monitor is set to True Color (32-bit) in\n"
 720			"Control Panels -> Display -> Settings.\n"
 721			"If you continue to receive this message, contact customer service.",
 722			"Error",
 723			OSMB_OK);
 724		return FALSE;
 725	}
 726#endif
 727
 728#if LL_X11
 729	/* Grab the window manager specific information */
 730	SDL_SysWMinfo info;
 731	SDL_VERSION(&info.version);
 732	if ( SDL_GetWMInfo(&info) )
 733	{
 734		/* Save the information for later use */
 735		if ( info.subsystem == SDL_SYSWM_X11 )
 736		{
 737			mSDL_Display = info.info.x11.display;
 738			mSDL_XWindowID = info.info.x11.wmwindow;
 739			Lock_Display = info.info.x11.lock_func;
 740			Unlock_Display = info.info.x11.unlock_func;
 741		}
 742		else
 743		{
 744			llwarns << "We're not running under X11?  Wild."
 745				<< llendl;
 746		}
 747	}
 748	else
 749	{
 750		llwarns << "We're not running under any known WM.  Wild."
 751			<< llendl;
 752	}
 753#endif // LL_X11
 754
 755
 756	//make sure multisampling is disabled by default
 757	glDisable(GL_MULTISAMPLE_ARB);
 758	
 759	// We need to do this here, once video is init'd
 760	if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
 761				      SDL_DEFAULT_REPEAT_INTERVAL))
 762	    llwarns << "Couldn't enable key-repeat: " << SDL_GetError() <<llendl;
 763
 764	// Don't need to get the current gamma, since there's a call that restores it to the system defaults.
 765	return TRUE;
 766}
 767
 768
 769// changing fullscreen resolution, or switching between windowed and fullscreen mode.
 770BOOL LLWindowSDL::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
 771{
 772	const BOOL needsRebuild = TRUE;  // Just nuke the context and start over.
 773	BOOL result = true;
 774
 775	llinfos << "switchContext, fullscreen=" << fullscreen << llendl;
 776	stop_glerror();
 777	if(needsRebuild)
 778	{
 779		destroyContext();
 780		result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
 781		if (result)
 782		{
 783			gGLManager.initGL();
 784
 785			//start with arrow cursor
 786			initCursors();
 787			setCursor( UI_CURSOR_ARROW );
 788		}
 789	}
 790
 791	stop_glerror();
 792
 793	return result;
 794}
 795
 796void LLWindowSDL::destroyContext()
 797{
 798	llinfos << "destroyContext begins" << llendl;
 799
 800#if LL_X11
 801	mSDL_Display = NULL;
 802	mSDL_XWindowID = None;
 803	Lock_Display = NULL;
 804	Unlock_Display = NULL;
 805#endif // LL_X11
 806
 807	// Clean up remaining GL state before blowing away window
 808	llinfos << "shutdownGL begins" << llendl;
 809	gGLManager.shutdownGL();
 810	llinfos << "SDL_QuitSS/VID begins" << llendl;
 811	SDL_QuitSubSystem(SDL_INIT_VIDEO);  // *FIX: this might be risky...
 812
 813	mWindow = NULL;
 814}
 815
 816LLWindowSDL::~LLWindowSDL()
 817{
 818	quitCursors();
 819	destroyContext();
 820
 821	if(mSupportedResolutions != NULL)
 822	{
 823		delete []mSupportedResolutions;
 824	}
 825
 826	gWindowImplementation = NULL;
 827}
 828
 829
 830void LLWindowSDL::show()
 831{
 832    // *FIX: What to do with SDL?
 833}
 834
 835void LLWindowSDL::hide()
 836{
 837    // *FIX: What to do with SDL?
 838}
 839
 840//virtual
 841void LLWindowSDL::minimize()
 842{
 843    // *FIX: What to do with SDL?
 844}
 845
 846//virtual
 847void LLWindowSDL::restore()
 848{
 849    // *FIX: What to do with SDL?
 850}
 851
 852
 853// close() destroys all OS-specific code associated with a window.
 854// Usually called from LLWindowManager::destroyWindow()
 855void LLWindowSDL::close()
 856{
 857	// Is window is already closed?
 858	//	if (!mWindow)
 859	//	{
 860	//		return;
 861	//	}
 862
 863	// Make sure cursor is visible and we haven't mangled the clipping state.
 864	setMouseClipping(FALSE);
 865	showCursor();
 866
 867	destroyContext();
 868}
 869
 870BOOL LLWindowSDL::isValid()
 871{
 872	return (mWindow != NULL);
 873}
 874
 875BOOL LLWindowSDL::getVisible()
 876{
 877	BOOL result = FALSE;
 878
 879    // *FIX: This isn't really right...
 880	// Then what is?
 881	if (mWindow)
 882	{
 883		result = TRUE;
 884	}
 885
 886	return(result);
 887}
 888
 889BOOL LLWindowSDL::getMinimized()
 890{
 891	BOOL result = FALSE;
 892
 893	if (mWindow && (1 == mIsMinimized))
 894	{
 895		result = TRUE;
 896	}
 897	return(result);
 898}
 899
 900BOOL LLWindowSDL::getMaximized()
 901{
 902	BOOL result = FALSE;
 903
 904	if (mWindow)
 905	{
 906		// TODO
 907	}
 908
 909	return(result);
 910}
 911
 912BOOL LLWindowSDL::maximize()
 913{
 914	// TODO
 915	return FALSE;
 916}
 917
 918BOOL LLWindowSDL::getFullscreen()
 919{
 920	return mFullscreen;
 921}
 922
 923BOOL LLWindowSDL::getPosition(LLCoordScreen *position)
 924{
 925    // *FIX: can anything be done with this?
 926	position->mX = 0;
 927	position->mY = 0;
 928    return TRUE;
 929}
 930
 931BOOL LLWindowSDL::getSize(LLCoordScreen *size)
 932{
 933    if (mWindow)
 934    {
 935        size->mX = mWindow->w;
 936        size->mY = mWindow->h;
 937	return (TRUE);
 938    }
 939
 940    return (FALSE);
 941}
 942
 943BOOL LLWindowSDL::getSize(LLCoordWindow *size)
 944{
 945    if (mWindow)
 946    {
 947        size->mX = mWindow->w;
 948        size->mY = mWindow->h;
 949	return (TRUE);
 950    }
 951
 952    return (FALSE);
 953}
 954
 955BOOL LLWindowSDL::setPosition(const LLCoordScreen position)
 956{
 957	if(mWindow)
 958	{
 959        // *FIX: (???)
 960		//MacMoveWindow(mWindow, position.mX, position.mY, false);
 961	}
 962
 963	return TRUE;
 964}
 965
 966BOOL LLWindowSDL::setSizeImpl(const LLCoordScreen size)
 967{
 968	if(mWindow)
 969	{
 970		// Push a resize event onto SDL's queue - we'll handle it
 971		// when it comes out again.
 972		SDL_Event event;
 973		event.type = SDL_VIDEORESIZE;
 974		event.resize.w = size.mX;
 975		event.resize.h = size.mY;
 976		SDL_PushEvent(&event); // copied into queue
 977
 978		return TRUE;
 979	}
 980		
 981	return FALSE;
 982}
 983
 984void LLWindowSDL::swapBuffers()
 985{
 986	if (mWindow)
 987	{	
 988		SDL_GL_SwapBuffers();
 989	}
 990}
 991
 992U32 LLWindowSDL::getFSAASamples()
 993{
 994	return mFSAASamples;
 995}
 996
 997void LLWindowSDL::setFSAASamples(const U32 samples)
 998{
 999	mFSAASamples = samples;
1000}
1001
1002F32 LLWindowSDL::getGamma()
1003{
1004	return 1/mGamma;
1005}
1006
1007BOOL LLWindowSDL::restoreGamma()
1008{
1009	//CGDisplayRestoreColorSyncSettings();
1010    SDL_SetGamma(1.0f, 1.0f, 1.0f);
1011	return true;
1012}
1013
1014BOOL LLWindowSDL::setGamma(const F32 gamma)
1015{
1016	mGamma = gamma;
1017	if (mGamma == 0) mGamma = 0.1f;
1018	mGamma = 1/mGamma;
1019	SDL_SetGamma(mGamma, mGamma, mGamma);
1020	return true;
1021}
1022
1023BOOL LLWindowSDL::isCursorHidden()
1024{
1025	return mCursorHidden;
1026}
1027
1028
1029
1030// Constrains the mouse to the window.
1031void LLWindowSDL::setMouseClipping( BOOL b )
1032{
1033    //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
1034}
1035
1036// virtual
1037void LLWindowSDL::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately)
1038{
1039	LLWindow::setMinSize(min_width, min_height, enforce_immediately);
1040
1041#if LL_X11
1042	// Set the minimum size limits for X11 window
1043	// so the window manager doesn't allow resizing below those limits.
1044	XSizeHints* hints = XAllocSizeHints();
1045	hints->flags |= PMinSize;
1046	hints->min_width = mMinWindowWidth;
1047	hints->min_height = mMinWindowHeight;
1048
1049	XSetWMNormalHints(mSDL_Display, mSDL_XWindowID, hints);
1050
1051	XFree(hints);
1052#endif
1053}
1054
1055BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position)
1056{
1057	BOOL result = TRUE;
1058	LLCoordScreen screen_pos;
1059
1060	if (!convertCoords(position, &screen_pos))
1061	{
1062		return FALSE;
1063	}
1064
1065	//llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
1066
1067	// do the actual forced cursor move.
1068	SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
1069	
1070	//llinfos << llformat("llcw %d,%d -> scr %d,%d", position.mX, position.mY, screen_pos.mX, screen_pos.mY) << llendl;
1071
1072	return result;
1073}
1074
1075BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position)
1076{
1077	//Point cursor_point;
1078	LLCoordScreen screen_pos;
1079
1080	//GetMouse(&cursor_point);
1081    int x, y;
1082    SDL_GetMouseState(&x, &y);
1083
1084	screen_pos.mX = x;
1085	screen_pos.mY = y;
1086
1087	return convertCoords(screen_pos, position);
1088}
1089
1090
1091F32 LLWindowSDL::getNativeAspectRatio()
1092{
1093#if 0
1094	// RN: this hack presumes that the largest supported resolution is monitor-limited
1095	// and that pixels in that mode are square, therefore defining the native aspect ratio
1096	// of the monitor...this seems to work to a close approximation for most CRTs/LCDs
1097	S32 num_resolutions;
1098	LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
1099
1100
1101	return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
1102	//rn: AC
1103#endif
1104
1105	// MBW -- there are a couple of bad assumptions here.  One is that the display list won't include
1106	//		ridiculous resolutions nobody would ever use.  The other is that the list is in order.
1107
1108	// New assumptions:
1109	// - pixels are square (the only reasonable choice, really)
1110	// - The user runs their display at a native resolution, so the resolution of the display
1111	//    when the app is launched has an aspect ratio that matches the monitor.
1112
1113	//RN: actually, the assumption that there are no ridiculous resolutions (above the display's native capabilities) has 
1114	// been born out in my experience.  
1115	// Pixels are often not square (just ask the people who run their LCDs at 1024x768 or 800x600 when running fullscreen, like me)
1116	// The ordering of display list is a blind assumption though, so we should check for max values
1117	// Things might be different on the Mac though, so I'll defer to MBW
1118
1119	// The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
1120	// switching, and stashes it in mOriginalAspectRatio.  Here, we just return it.
1121
1122	if (mOverrideAspectRatio > 0.f)
1123	{
1124		return mOverrideAspectRatio;
1125	}
1126
1127	return mOriginalAspectRatio;
1128}
1129
1130F32 LLWindowSDL::getPixelAspectRatio()
1131{
1132	F32 pixel_aspect = 1.f;
1133	if (getFullscreen())
1134	{
1135		LLCoordScreen screen_size;
1136		if (getSize(&screen_size))
1137		{
1138			pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
1139		}
1140	}
1141
1142	return pixel_aspect;
1143}
1144
1145
1146// This is to support 'temporarily windowed' mode so that
1147// dialogs are still usable in fullscreen.
1148void LLWindowSDL::beforeDialog()
1149{
1150	bool running_x11 = false;
1151#if LL_X11
1152	running_x11 = (mSDL_XWindowID != None);
1153#endif //LL_X11
1154
1155	llinfos << "LLWindowSDL::beforeDialog()" << llendl;
1156
1157	if (SDLReallyCaptureInput(FALSE)) // must ungrab input so popup works!
1158	{
1159		if (mFullscreen)
1160		{
1161			// need to temporarily go non-fullscreen; bless SDL
1162			// for providing a SDL_WM_ToggleFullScreen() - though
1163			// it only works in X11
1164			if (running_x11 && mWindow)
1165			{
1166				SDL_WM_ToggleFullScreen(mWindow);
1167			}
1168		}
1169	}
1170
1171#if LL_X11
1172	if (mSDL_Display)
1173	{
1174		// Everything that we/SDL asked for should happen before we
1175		// potentially hand control over to GTK.
1176		maybe_lock_display();
1177		XSync(mSDL_Display, False);
1178		maybe_unlock_display();
1179	}
1180#endif // LL_X11
1181
1182#if LL_GTK
1183	// this is a good time to grab some GTK version information for
1184	// diagnostics, if not already done.
1185	ll_try_gtk_init();
1186#endif // LL_GTK
1187
1188	maybe_lock_display();
1189}
1190
1191void LLWindowSDL::afterDialog()
1192{
1193	bool running_x11 = false;
1194#if LL_X11
1195	running_x11 = (mSDL_XWindowID != None);
1196#endif //LL_X11
1197
1198	llinfos << "LLWindowSDL::afterDialog()" << llendl;
1199
1200	maybe_unlock_display();
1201
1202	if (mFullscreen)
1203	{
1204		// need to restore fullscreen mode after dialog - only works
1205		// in X11
1206		if (running_x11 && mWindow)
1207		{
1208			SDL_WM_ToggleFullScreen(mWindow);
1209		}
1210	}
1211}
1212
1213
1214#if LL_X11
1215// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
1216void LLWindowSDL::x11_set_urgent(BOOL urgent)
1217{
1218	if (mSDL_Display && !mFullscreen)
1219	{
1220		XWMHints *wm_hints;
1221		
1222		llinfos << "X11 hint for urgency, " << urgent << llendl;
1223
1224		maybe_lock_display();
1225		wm_hints = XGetWMHints(mSDL_Display, mSDL_XWindowID);
1226		if (!wm_hints)
1227			wm_hints = XAllocWMHints();
1228
1229		if (urgent)
1230			wm_hints->flags |= XUrgencyHint;
1231		else
1232			wm_hints->flags &= ~XUrgencyHint;
1233
1234		XSetWMHints(mSDL_Display, mSDL_XWindowID, wm_hints);
1235		XFree(wm_hints);
1236		XSync(mSDL_Display, False);
1237		maybe_unlock_display();
1238	}
1239}
1240#endif // LL_X11
1241
1242void LLWindowSDL::flashIcon(F32 seconds)
1243{
1244#if !LL_X11
1245	llinfos << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
1246#else	
1247	llinfos << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
1248	
1249	F32 remaining_time = mFlashTimer.getRemainingTimeF32();
1250	if (remaining_time < seconds)
1251		remaining_time = seconds;
1252	mFlashTimer.reset();
1253	mFlashTimer.setTimerExpirySec(remaining_time);
1254
1255	x11_set_urgent(TRUE);
1256	mFlashing = TRUE;
1257#endif // LL_X11
1258}
1259
1260
1261#if LL_GTK
1262BOOL LLWindowSDL::isClipboardTextAvailable()
1263{
1264	if (ll_try_gtk_init())
1265	{
1266		GtkClipboard * const clipboard =
1267			gtk_clipboard_get(GDK_NONE);
1268		return gtk_clipboard_wait_is_text_available(clipboard) ?
1269			TRUE : FALSE;
1270	}
1271	return FALSE; // failure
1272}
1273
1274BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &text)
1275{
1276	if (ll_try_gtk_init())
1277	{
1278		GtkClipboard * const clipboard =
1279			gtk_clipboard_get(GDK_NONE);
1280		gchar * const data = gtk_clipboard_wait_for_text(clipboard);
1281		if (data)
1282		{
1283			text = LLWString(utf8str_to_wstring(data));
1284			g_free(data);
1285			return TRUE;
1286		}
1287	}
1288	return FALSE; // failure
1289}
1290
1291BOOL LLWindowSDL::copyTextToClipboard(const LLWString &text)
1292{
1293	if (ll_try_gtk_init())
1294	{
1295		const std::string utf8 = wstring_to_utf8str(text);
1296		GtkClipboard * const clipboard =
1297			gtk_clipboard_get(GDK_NONE);
1298		gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length());
1299		return TRUE;
1300	}
1301	return FALSE; // failure
1302}
1303
1304
1305BOOL LLWindowSDL::isPrimaryTextAvailable()
1306{
1307	if (ll_try_gtk_init())
1308	{
1309		GtkClipboard * const clipboard =
1310			gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1311		return gtk_clipboard_wait_is_text_available(clipboard) ?
1312			TRUE : FALSE;
1313	}
1314	return FALSE; // failure
1315}
1316
1317BOOL LLWindowSDL::pasteTextFromPrimary(LLWString &text)
1318{
1319	if (ll_try_gtk_init())
1320	{
1321		GtkClipboard * const clipboard =
1322			gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1323		gchar * const data = gtk_clipboard_wait_for_text(clipboard);
1324		if (data)
1325		{
1326			text = LLWString(utf8str_to_wstring(data));
1327			g_free(data);
1328			return TRUE;
1329		}
1330	}
1331	return FALSE; // failure
1332}
1333
1334BOOL LLWindowSDL::copyTextToPrimary(const LLWString &text)
1335{
1336	if (ll_try_gtk_init())
1337	{
1338		const std::string utf8 = wstring_to_utf8str(text);
1339		GtkClipboard * const clipboard =
1340			gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1341		gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length());
1342		return TRUE;
1343	}
1344	return FALSE; // failure
1345}
1346
1347#else
1348
1349BOOL LLWindowSDL::isClipboardTextAvailable()
1350{
1351	return FALSE; // unsupported
1352}
1353
1354BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
1355{
1356	return FALSE; // unsupported
1357}
1358
1359BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
1360{
1361	return FALSE;  // unsupported
1362}
1363
1364BOOL LLWindowSDL::isPrimaryTextAvailable()
1365{
1366	return FALSE; // unsupported
1367}
1368
1369BOOL LLWindowSDL::pasteTextFromPrimary(LLWString &dst)
1370{
1371	return FALSE; // unsupported
1372}
1373
1374BOOL LLWindowSDL::copyTextToPrimary(const LLWString &s)
1375{
1376	return FALSE;  // unsupported
1377}
1378
1379#endif // LL_GTK
1380
1381LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
1382{
1383	if (!mSupportedResolutions)
1384	{
1385		mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
1386		mNumSupportedResolutions = 0;
1387
1388        SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
1389        if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
1390        {
1391            int count = 0;
1392            while (*modes && count<MAX_NUM_RESOLUTIONS)  // they're sorted biggest to smallest, so find end...
1393            {
1394                modes++;
1395                count++;
1396            }
1397
1398            while (count--)
1399            {
1400                modes--;
1401                SDL_Rect *r = *modes;
1402                int w = r->w;
1403                int h = r->h;
1404                if ((w >= 800) && (h >= 600))
1405                {
1406                    // make sure we don't add the same resolution multiple times!
1407                    if ( (mNumSupportedResolutions == 0) ||
1408                         ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
1409                          (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
1410                    {
1411                        mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
1412                        mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
1413                        mNumSupportedResolutions++;
1414                    }
1415                }
1416            }
1417        }
1418	}
1419
1420	num_resolutions = mNumSupportedResolutions;
1421	return mSupportedResolutions;
1422}
1423
1424BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
1425{
1426    if (!to)
1427        return FALSE;
1428
1429	to->mX = from.mX;
1430	to->mY = mWindow->h - from.mY - 1;
1431
1432	return TRUE;
1433}
1434
1435BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
1436{
1437    if (!to)
1438        return FALSE;
1439
1440	to->mX = from.mX;
1441	to->mY = mWindow->h - from.mY - 1;
1442
1443	return TRUE;
1444}
1445
1446BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
1447{
1448    if (!to)
1449		return FALSE;
1450
1451	// In the fullscreen case, window and screen coordinates are the same.
1452	to->mX = from.mX;
1453	to->mY = from.mY;
1454    return (TRUE);
1455}
1456
1457BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
1458{
1459    if (!to)
1460		return FALSE;
1461
1462	// In the fullscreen case, window and screen coordinates are the same.
1463	to->mX = from.mX;
1464	to->mY = from.mY;
1465    return (TRUE);
1466}
1467
1468BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
1469{
1470	LLCoordWindow window_coord;
1471
1472	return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
1473}
1474
1475BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
1476{
1477	LLCoordWindow window_coord;
1478
1479	return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
1480}
1481
1482
1483
1484
1485void LLWindowSDL::setupFailure(const std::string& text, const std::string& caption, U32 type)
1486{
1487	destroyContext();
1488
1489	OSMessageBox(text, caption, type);
1490}
1491
1492BOOL LLWindowSDL::SDLReallyCaptureInput(BOOL capture)
1493{
1494	// note: this used to be safe to call nestedly, but in the
1495	// end that's not really a wise usage pattern, so don't.
1496
1497	if (capture)
1498		mReallyCapturedCount = 1;
1499	else
1500		mReallyCapturedCount = 0;
1501	
1502	SDL_GrabMode wantmode, newmode;
1503	if (mReallyCapturedCount <= 0) // uncapture
1504	{
1505		wantmode = SDL_GRAB_OFF;
1506	} else // capture
1507	{
1508		wantmode = SDL_GRAB_ON;
1509	}
1510	
1511	if (mReallyCapturedCount < 0) // yuck, imbalance.
1512	{
1513		mReallyCapturedCount = 0;
1514		llwarns << "ReallyCapture count was < 0" << llendl;
1515	}
1516
1517	if (!mFullscreen) /* only bother if we're windowed anyway */
1518	{
1519#if LL_X11
1520		if (mSDL_Display)
1521		{
1522			/* we dirtily mix raw X11 with SDL so that our pointer
1523			   isn't (as often) constrained to the limits of the
1524			   window while grabbed, which feels nicer and
1525			   hopefully eliminates some reported 'sticky pointer'
1526			   problems.  We use raw X11 instead of
1527			   SDL_WM_GrabInput() because the latter constrains
1528			   the pointer to the window and also steals all
1529			   *keyboard* input from the window manager, which was
1530			   frustrating users. */
1531			int result;
1532			if (wantmode == SDL_GRAB_ON)
1533			{
1534				//llinfos << "X11 POINTER GRABBY" << llendl;
1535				//newmode = SDL_WM_GrabInput(wantmode);
1536				maybe_lock_display();
1537				result = XGrabPointer(mSDL_Display, mSDL_XWindowID,
1538						      True, 0, GrabModeAsync,
1539						      GrabModeAsync,
1540						      None, None, CurrentTime);
1541				maybe_unlock_display();
1542				if (GrabSuccess == result)
1543					newmode = SDL_GRAB_ON;
1544				else
1545					newmode = SDL_GRAB_OFF;
1546			} else if (wantmode == SDL_GRAB_OFF)
1547			{
1548				//llinfos << "X11 POINTER UNGRABBY" << llendl;
1549				newmode = SDL_GRAB_OFF;
1550				//newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
1551				
1552				maybe_lock_display();
1553				XUngrabPointer(mSDL_Display, CurrentTime);
1554				// Make sure the ungrab happens RIGHT NOW.
1555				XSync(mSDL_Display, False);
1556				maybe_unlock_display();
1557			} else
1558			{
1559				newmode = SDL_GRAB_QUERY; // neutral
1560			}
1561		} else // not actually running on X11, for some reason
1562			newmode = wantmode;
1563#endif // LL_X11
1564	} else {
1565		// pretend we got what we wanted, when really we don't care.
1566		newmode = wantmode;
1567	}
1568	
1569	// return boolean success for whether we ended up in the desired state
1570	return (capture && SDL_GRAB_ON==newmode) ||
1571		(!capture && SDL_GRAB_OFF==newmode);
1572}
1573
1574U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain)
1575{
1576	/* part of the fix for SL-13243: Some popular window managers like
1577	   to totally eat alt-drag for the purposes of moving windows.  We
1578	   spoil their day by acquiring the exclusive X11 mouse lock for as
1579	   long as ALT is held down, so the window manager can't easily
1580	   see what's happening.  Tested successfully with Metacity.
1581	   And... do the same with CTRL, for other darn WMs.  We don't
1582	   care about other metakeys as SL doesn't use them with dragging
1583	   (for now). */
1584
1585	/* We maintain a bitmap of critical keys which are up and down
1586	   instead of simply key-counting, because SDL sometimes reports
1587	   misbalanced keyup/keydown event pairs to us for whatever reason. */
1588
1589	U32 mask = 0;
1590	switch (keysym)
1591	{
1592	case SDLK_LALT:
1593		mask = 1U << 0; break;
1594	case SDLK_RALT:
1595		mask = 1U << 1; break;
1596	case SDLK_LCTRL:
1597		mask = 1U << 2; break;
1598	case SDLK_RCTRL:
1599		mask = 1U << 3; break;
1600	default:
1601		break;
1602	}
1603
1604	if (gain)
1605		mGrabbyKeyFlags |= mask;
1606	else
1607		mGrabbyKeyFlags &= ~mask;
1608
1609	//llinfos << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << llendl;
1610
1611	/* 0 means we don't need to mousegrab, otherwise grab. */
1612	return mGrabbyKeyFlags;
1613}
1614
1615
1616void check_vm_bloat()
1617{
1618#if LL_LINUX
1619	// watch our own VM and RSS sizes, warn if we bloated rapidly
1620	FILE *fp = fopen("/proc/self/stat", "r");
1621	if (fp)
1622	{
1623		static long long last_vm_size = 0;
1624		static long long last_rss_size = 0;
1625		const long long significant_vm_difference = 250 * 1024*1024;
1626		const long long significant_rss_difference = 50 * 1024*1024;
1627
1628		ssize_t res;
1629		size_t dummy;
1630		char *ptr;
1631		for (int i=0; i<22; ++i) // parse past the values we don't want
1632		{
1633			ptr = NULL;
1634			res = getdelim(&ptr, &dummy, ' ', fp);
1635			free(ptr);
1636		}
1637		// 23rd space-delimited entry is vsize
1638		ptr = NULL;
1639		res = getdelim(&ptr, &dummy, ' ', fp);
1640		llassert(ptr);
1641		long long this_vm_size = atoll(ptr);
1642		free(ptr);
1643		// 24th space-delimited entry is RSS
1644		ptr = NULL;
1645		res = getdelim(&ptr, &dummy, ' ', fp);
1646		llassert(ptr);
1647		long long this_rss_size = getpagesize() * atoll(ptr);
1648		free(ptr);
1649
1650		llinfos << "VM SIZE IS NOW " << (this_vm_size/(1024*1024)) << " MB, RSS SIZE IS NOW " << (this_rss_size/(1024*1024)) << " MB" << llendl;
1651
1652		if (llabs(last_vm_size - this_vm_size) >
1653		    significant_vm_difference)
1654		{
1655			if (this_vm_size > last_vm_size)
1656			{
1657				llwarns << "VM size grew by " << (this_vm_size - last_vm_size)/(1024*1024) << " MB in last frame" << llendl;
1658			}
1659			else
1660			{
1661				llinfos << "VM size shrank by " << (last_vm_size - this_vm_size)/(1024*1024) << " MB in last frame" << llendl;
1662			}
1663		}
1664
1665		if (llabs(last_rss_size - this_rss_size) >
1666		    significant_rss_difference)
1667		{
1668			if (this_rss_size > last_rss_size)
1669			{
1670				llwarns << "RSS size grew by " << (this_rss_size - last_rss_size)/(1024*1024) << " MB in last frame" << llendl;
1671			}
1672			else
1673			{
1674				llinfos << "RSS size shrank by " << (last_rss_size - this_rss_size)/(1024*1024) << " MB in last frame" << llendl;
1675			}
1676		}
1677
1678		last_rss_size = this_rss_size;
1679		last_vm_size = this_vm_size;
1680
1681		fclose(fp);
1682	}
1683#endif // LL_LINUX
1684}
1685
1686
1687// virtual
1688void LLWindowSDL::processMiscNativeEvents()
1689{
1690#if LL_GTK
1691	// Pump GTK events to avoid starvation for:
1692	// * DBUS servicing
1693	// * Anything else which quietly hooks into the default glib/GTK loop
1694    if (ll_try_gtk_init())
1695    {
1696	    // Yuck, Mozilla's GTK callbacks play with the locale - push/pop
1697	    // the locale to protect it, as exotic/non-C locales
1698	    // causes our code lots of general critical weirdness
1699	    // and crashness. (SL-35450)
1700	    static std::string saved_locale;
1701	    saved_locale = ll_safe_string(setlocale(LC_ALL, NULL));
1702
1703	    // Pump until we've nothing left to do or passed 1/15th of a
1704	    // second pumping for this frame.
1705	    static LLTimer pump_timer;
1706	    pump_timer.reset();
1707	    pump_timer.setTimerExpirySec(1.0f / 15.0f);
1708	    do {
1709		     // Always do at least one non-blocking pump
1710		    gtk_main_iteration_do(FALSE);
1711	    } while (gtk_events_pending() &&
1712		     !pump_timer.hasExpired());
1713
1714	    setlocale(LC_ALL, saved_locale.c_str() );
1715    }
1716#endif // LL_GTK
1717
1718    // hack - doesn't belong here - but this is just for debugging
1719    if (getenv("LL_DEBUG_BLOAT"))
1720    {
1721	    check_vm_bloat();
1722    }
1723}
1724
1725void LLWindowSDL::gatherInput()
1726{
1727    const Uint32 CLICK_THRESHOLD = 300;  // milliseconds
1728    static int leftClick = 0;
1729    static int rightClick = 0;
1730    static Uint32 lastLeftDown = 0;
1731    static Uint32 lastRightDown = 0;
1732    SDL_Event event;
1733
1734    // Handle all outstanding SDL events
1735    while (SDL_PollEvent(&event))
1736    {
1737        switch (event.type)
1738        {
1739            case SDL_MOUSEMOTION:
1740            {
1741                LLCoordWindow winCoord(event.button.x, event.button.y);
1742                LLCoordGL openGlCoord;
1743                convertCoords(winCoord, &openGlCoord);
1744				MASK mask = gKeyboard->currentMask(TRUE);
1745				mCallbacks->handleMouseMove(this, openGlCoord, mask);
1746                break;
1747            }
1748
1749            case SDL_KEYDOWN:
1750		    mKeyScanCode = event.key.keysym.scancode;
1751		    mKeyVirtualKey = event.key.keysym.unicode;
1752		    mKeyModifiers = event.key.keysym.mod;
1753
1754		    gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod);
1755		    // part of the fix for SL-13243
1756		    if (SDLCheckGrabbyKeys(event.key.keysym.sym, TRUE) != 0)
1757			    SDLReallyCaptureInput(TRUE);
1758
1759		    if (event.key.keysym.unicode)
1760		    {
1761			    handleUnicodeUTF16(event.key.keysym.unicode,
1762					       gKeyboard->currentMask(FALSE));
1763		    }
1764                break;
1765
1766            case SDL_KEYUP:
1767		    mKeyScanCode = event.key.keysym.scancode;
1768		    mKeyVirtualKey = event.key.keysym.unicode;
1769		    mKeyModifiers = event.key.keysym.mod;
1770
1771		    if (SDLCheckGrabbyKeys(event.key.keysym.sym, FALSE) == 0)
1772			    SDLReallyCaptureInput(FALSE); // part of the fix for SL-13243
1773
1774		    gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod);
1775		    break;
1776
1777            case SDL_MOUSEBUTTONDOWN:
1778            {
1779                bool isDoubleClick = false;
1780                LLCoordWindow winCoord(event.button.x, event.button.y);
1781                LLCoordGL openGlCoord;
1782                convertCoords(winCoord, &openGlCoord);
1783		MASK mask = gKeyboard->currentMask(TRUE);
1784
1785                if (event.button.button == SDL_BUTTON_LEFT)   // SDL doesn't manage double clicking...
1786                {
1787                    Uint32 now = SDL_GetTicks();
1788                    if ((now - lastLeftDown) > CLICK_THRESHOLD)
1789                        leftClick = 1;
1790                    else
1791                    {
1792                        if (++leftClick >= 2)
1793                        {
1794                            leftClick = 0;
1795			    isDoubleClick = true;
1796                        }
1797                    }
1798                    lastLeftDown = now;
1799                }
1800                else if (event.button.button == SDL_BUTTON_RIGHT)
1801                {
1802                    Uint32 now = SDL_GetTicks();
1803                    if ((now - lastRightDown) > CLICK_THRESHOLD)
1804                        rightClick = 1;
1805                    else
1806                    {
1807                        if (++rightClick >= 2)
1808                        {
1809                            rightClick = 0;
1810    					    isDoubleClick = true;
1811                        }
1812                    }
1813                    lastRightDown = now;
1814                }
1815
1816                if (event.button.button == SDL_BUTTON_LEFT)  // left
1817                {
1818                    if (isDoubleClick)
1819				        mCallbacks->handleDoubleClick(this, openGlCoord, mask);
1820                    else
1821    				    mCallbacks->handleMouseDown(this, openGlCoord, mask);
1822                }
1823
1824                else if (event.button.button == SDL_BUTTON_RIGHT)  // right
1825                {
1826			mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
1827                }
1828
1829                else if (event.button.button == SDL_BUTTON_MIDDLE)  // middle
1830				{
1831				    mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
1832				}
1833                else if (event.button.button == 4)  // mousewheel up...thanks to X11 for making SDL consider these "buttons".
1834					mCallbacks->handleScrollWheel(this, -1);
1835                else if (event.button.button == 5)  // mousewheel down...thanks to X11 for making SDL consider these "buttons".
1836					mCallbacks->handleScrollWheel(this, 1);
1837
1838                break;
1839            }
1840
1841            case SDL_MOUSEBUTTONUP:
1842            {
1843                LLCoordWindow winCoord(event.button.x, event.button.y);
1844                LLCoordGL openGlCoord;
1845                convertCoords(winCoord, &openGlCoord);
1846		MASK mask = gKeyboard->currentMask(TRUE);
1847
1848                if (event.button.button == SDL_BUTTON_LEFT)  // left
1849			mCallbacks->handleMouseUp(this, openGlCoord, mask);
1850                else if (event.button.button == SDL_BUTTON_RIGHT)  // right
1851			mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
1852                else if (event.button.button == SDL_BUTTON_MIDDLE)  // middle
1853			mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
1854                // don't handle mousewheel here...
1855
1856                break;
1857            }
1858
1859            case SDL_VIDEOEXPOSE:  // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing!
1860			    mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h);
1861                break;
1862
1863            case SDL_VIDEORESIZE:  // *FIX: handle this?
1864            {
1865		llinfos << "Handling a resize event: " << event.resize.w <<
1866			"x" << event.resize.h << llendl;
1867
1868		S32 width = llmax(event.resize.w, (S32)mMinWindowWidth);
1869		S32 height = llmax(event.resize.h, (S32)mMinWindowHeight);
1870
1871		// *FIX: I'm not sure this is necessary!
1872		mWindow = SDL_SetVideoMode(width, height, 32, mSDLFlags);
1873		if (!mWindow)
1874		{
1875			// *FIX: More informative dialog?
1876			llinfos << "Could not recreate context after resize! Quitting..." << llendl;
1877			if(mCallbacks->handleCloseRequest(this))
1878    			{
1879    				// Get the app to initiate cleanup.
1880    				mCallbacks->handleQuit(this);
1881    				// The app is responsible for calling destroyWindow when done with GL
1882    			}
1883                break;
1884		}
1885
1886		mCallbacks->handleResize(this, w

Large files files are truncated, but you can click here to view the full file