Index: indra/newview/linux_tools/wrapper.sh
===================================================================
--- indra/newview/linux_tools/wrapper.sh	(revision 81254)
+++ indra/newview/linux_tools/wrapper.sh	(revision 81255)
@@ -57,6 +57,14 @@
     export GTK_IM_MODULE=xim
 fi
 
+## - Second Life requires C locale compatible numeric formatting, while GTK prefers to run under the user's locale.  Find a compromise.
+if [ "$LC_ALL" != "" ]; then
+    export LANG="$LC_ALL"
+    unset `env | sed -n "/^LC_/s/=.*//p"`
+fi
+if [ "$LANG" != "" -o "$LC_NUMERIC" != "" ]; then
+    export LC_NUMERIC="C"
+fi
 
 ## Nothing worth editing below this line.
 ##-------------------------------------------------------------------
Index: indra/newview/llfilepicker.h
===================================================================
--- indra/newview/llfilepicker.h	(revision 81254)
+++ indra/newview/llfilepicker.h	(revision 81255)
@@ -63,11 +63,6 @@
 # include "gtk/gtk.h"
 #endif // LL_GTK
 
-// also mostly for Linux, for some X11-specific filepicker usability tweaks
-#if LL_X11
-#include "SDL/SDL_syswm.h"
-#endif
-
 #if LL_GTK
 // we use an aggregate structure so we can pass its pointer through a C callback
 typedef struct {
Index: indra/newview/llfilepicker.cpp
===================================================================
--- indra/newview/llfilepicker.cpp	(revision 81254)
+++ indra/newview/llfilepicker.cpp	(revision 81255)
@@ -1051,23 +1051,8 @@
 				 this_path->second.c_str());
 		}
 
-#  if LL_X11
-		// Make GTK tell the window manager to associate this
-		// dialog with our non-GTK raw X11 window, which should try
-		// to keep it on top etc.
-		Window XWindowID = get_SDL_XWindowID();
-		if (None != XWindowID)
-		{
-			gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
-			GdkWindow *gdkwin = gdk_window_foreign_new(XWindowID);
-			gdk_window_set_transient_for(GTK_WIDGET(win)->window,
-						     gdkwin);
-		}
-		else
-		{
-			llwarns << "Hmm, couldn't get xwid to use for transient." << llendl;
-		}
-#  endif //LL_X11
+		LLWindowSDL * const sdl_viewer_window = static_cast <LLWindowSDL *>(gViewerWindow->getWindow());
+		gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(sdl_viewer_window->getGtkWindow()));
 
 		g_signal_connect (GTK_FILE_CHOOSER(win),
 				  "response",
Index: indra/llwindow/llkeyboardsdl.h
===================================================================
--- indra/llwindow/llkeyboardsdl.h	(revision 81254)
+++ indra/llwindow/llkeyboardsdl.h	(revision 81255)
@@ -33,12 +33,14 @@
 #define LL_LLKEYBOARDSDL_H
 
 #include "llkeyboard.h"
-#include "SDL/SDL.h"
+#include "gtk/gtk.h"
 
+class LLWindowSDL;
+
 class LLKeyboardSDL : public LLKeyboard
 {
 public:
-	LLKeyboardSDL();
+	LLKeyboardSDL(LLWindowSDL *window);
 	/*virtual*/ ~LLKeyboardSDL() {};
 
 	/*virtual*/ BOOL	handleKeyUp(const U16 key, MASK mask);
@@ -52,6 +54,8 @@
 	void	setModifierKeyLevel( KEY key, BOOL new_state );
 	BOOL	translateNumpadKey( const U16 os_key, KEY *translated_key );
 	U16	inverseTranslateNumpadKey(const KEY translated_key);
+protected:
+	LLWindowSDL * const mWindow;
 private:
 	std::map<U16, KEY> mTranslateNumpadMap;  // special map for translating OS keys to numpad keys
 	std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above
Index: indra/llwindow/llwindowsdl.h
===================================================================
--- indra/llwindow/llwindowsdl.h	(revision 81254)
+++ indra/llwindow/llwindowsdl.h	(revision 81255)
@@ -35,15 +35,9 @@
 // Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class
 
 #include "llwindow.h"
+#include "llpreeditor.h"
+#include "gtk/gtk.h"
 
-#include "SDL/SDL.h"
-#include "SDL/SDL_endian.h"
-
-#if LL_X11
-// get X11-specific headers for use in low-level stuff like copy-and-paste support
-#include "SDL/SDL_syswm.h"
-#endif
-
 // AssertMacros.h does bad things.
 #undef verify
 #undef check
@@ -60,7 +54,6 @@
 	/*virtual*/ BOOL getMinimized();
 	/*virtual*/ BOOL getMaximized();
 	/*virtual*/ BOOL maximize();
-	/*virtual*/ BOOL getFullscreen();
 	/*virtual*/ BOOL getPosition(LLCoordScreen *position);
 	/*virtual*/ BOOL getSize(LLCoordScreen *size);
 	/*virtual*/ BOOL getSize(LLCoordWindow *size);
@@ -118,16 +111,12 @@
 	/*virtual*/ void *getPlatformWindow();
 	/*virtual*/ void bringToFront();
 
-	// Not great that these are public, but they have to be accessible
-	// by non-class code and it's better than making them global.
-#if LL_X11
-	// These are set up by the X11 clipboard initialization code
-	Window mSDL_XWindowID;
-	Display *mSDL_Display;
-#endif
-	void (*Lock_Display)(void);
-	void (*Unlock_Display)(void);
+	/*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, BOOL b);
+	/*virtual*/ void updateLanguageTextInputArea();
+	/*virtual*/ void interruptLanguageTextInput();
 
+	GtkWidget * getGtkWindow() const { return mWindow; }
+
 protected:
 	LLWindowSDL(
 		char *title, int x, int y, int width, int height, U32 flags,
@@ -140,72 +129,96 @@
 	BOOL	isValid();
 	void	moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
 
-
-	// Changes display resolution. Returns true if successful
-	BOOL	setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
-
-	// Go back to last fullscreen display resolution.
-	BOOL	setFullscreenResolution();
-
 	void	minimize();
 	void	restore();
 
 	BOOL	shouldPostQuit() { return mPostQuit; }
 
-
 protected:
 	//
 	// Platform specific methods
 	//
 
-	// create or re-create the GL context/window.  Called from the constructor and switchContext().
-	BOOL createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync);
+	// createContext creates the GL context/window.  Called from the constructor.
+	BOOL createContext(S32 x, S32 y, S32 width, S32 height, BOOL fullscreen, BOOL disable_vsync);
 	void destroyContext();
 	void setupFailure(const char* text, const char* caption, U32 type);
-	void adjustCursorDecouple(bool warpingMouse = false);
 	void fixWindowSize(void);
-	U32 SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain);
-	BOOL SDLReallyCaptureInput(BOOL capture);
+	void setupX11Extensions();
+	void initializeGamma();
 
+	void getMonitorResolution(S32 *width, S32 *height);
+	void switchMonitorResolution(S32 width, S32 height);
+	void enterFullscreenResolution();
+	void leaveFullscreenResolution();
+
+	U32 checkGrabbyKeys(guint keysym, BOOL gain);
+	void reallyCaptureInput(BOOL capture);
+
+	// input method management methods.
+	void createIMContext();
+	void destroyIMContext();
+	void updateIMFocus();
+
+public:
+	// GTK signal handlers.  We need to make them public, since we
+	// want to call them from C functions (C-C++ thunks).  We could
+	// make them private or protected otherwise.
+
+	// GtkWindow event handlers:
+	gboolean handleGtkMotionNotifyEvent  (GdkEventMotion *event);
+	gboolean handleGtkKeyPressEvent      (GdkEventKey *event);
+	gboolean handleGtkKeyReleaseEvent    (GdkEventKey *event);
+	gboolean handleGtkButtonPressEvent   (GdkEventButton *event);
+	gboolean handleGtkButtonReleaseEvent (GdkEventButton *event);
+	gboolean handleGtkScrollEvent        (GdkEventScroll *event);
+	gboolean handleGtkFocusInEvent       (GdkEventFocus *event);
+	gboolean handleGtkFocusOutEvent      (GdkEventFocus *event);
+	gboolean handleGtkExposeEvent        (GdkEventExpose *event);
+	gboolean handleGtkConfigureEvent     (GdkEventConfigure *event);
+	gboolean handleGtkWindowStateEvent   (GdkEventWindowState *event);
+	gboolean handleGtkDeleteEvent        (GdkEvent *event);
+
+	// Input method (GtkIMContext) signal handlers:
+	void     handleGtkCommitSignal              (const gchar *committed_text);
+	void     handleGtkPreeditChangedSignal      ();
+	void     handleGtkRetrieveSurroundingSignal ();
+	gboolean handleGtkDeleteSurroundingSignal   (gint offset, gint n_chars);
+
+protected:
 	//
 	// Platform specific variables
 	//
-	U32             mGrabbyKeyFlags;
-	int			mReallyCapturedCount;
-	SDL_Surface *	mWindow;
-	char * 		mWindowTitle;
-	double		mOriginalAspectRatio;
-	BOOL		mCursorDecoupled;
-	S32			mCursorLastEventDeltaX;
-	S32			mCursorLastEventDeltaY;
-	BOOL		mCursorIgnoreNextDelta;
-	BOOL		mNeedsResize;		// Constructor figured out the window is too big, it needs a resize.
-	LLCoordScreen   mNeedsResizeSize;
+	U32			mGrabbyKeyFlags;
+	std::string	mWindowTitle;
+	GtkWidget *	mWindow;
+	GtkWidget *	mGLWidget;
+	S32			mOriginalMonitorWidth;
+	S32			mOriginalMonitorHeight;
 	F32			mOverrideAspectRatio;
-	F32		mGamma;
+	F32			mGamma;
+	GdkCursor *	mHiddenCursor;
+	GdkCursor *	mCursors[UI_CURSOR_COUNT];
+	S32			mXRRVersion;
+	S32			mXF86VMVersion;
 
-	int		mSDLFlags;
-
-	SDL_Cursor*	mSDLCursors[UI_CURSOR_COUNT];
-	int             mHaveInputFocus; /* 0=no, 1=yes, else unknown */
-	int             mIsMinimized; /* 0=no, 1=yes, else unknown */
-
 	friend class LLWindowManager;
 
-#if LL_X11
-private:
-	// more X11 clipboard stuff
-	int init_x11clipboard(void);
-	void quit_x11clipboard(void);
-	int is_empty_x11clipboard(void);
-	void put_x11clipboard(int type, int srclen, const char *src);
-	void get_x11clipboard(int type, int *dstlen, char **dst);
-	void x11_set_urgent(BOOL urgent);
+protected:
 	BOOL mFlashing;
 	LLTimer mFlashTimer;
-#endif //LL_X11
 
-	
+protected:
+	// Member variables that keep track of input method states.
+	LLPreeditor	*mPreeditor;
+	LLWString	mPreeditText;
+	LLPreeditor::segment_lengths_t mPreeditSegmentLengths;
+	LLPreeditor::standouts_t mPreeditStandouts;
+	S32			mPreeditCaret;
+	GtkIMContext *mIMContext;	
+	BOOL		mWindowIsFocused;
+	BOOL		mIMContextIsFocused;
+	BOOL		mPrimaryClipboardNeedsReset;
 };
 
 
@@ -224,14 +237,7 @@
 
 void load_url_external(const char* url);
 
-#if LL_GTK
 // Lazily initialize and check the runtime GTK version for goodness.
 BOOL ll_try_gtk_init(void);
-#endif // LL_GTK
 
-#if LL_X11
-Window get_SDL_XWindowID(void);
-Display* get_SDL_Display(void);
-#endif // LL_X11
-
 #endif //LL_LLWINDOWSDL_H
Index: indra/llwindow/llkeyboardsdl.cpp
===================================================================
--- indra/llwindow/llkeyboardsdl.cpp	(revision 81254)
+++ indra/llwindow/llkeyboardsdl.cpp	(revision 81255)
@@ -33,10 +33,12 @@
 
 #include "linden_common.h"
 #include "llkeyboardsdl.h"
-#include "llwindow.h"
-#include "SDL/SDL.h"
+#include "llwindowsdl.h"
+#include "gtk/gtk.h"
+#include "gdk/gdkkeysyms.h"
 
-LLKeyboardSDL::LLKeyboardSDL()
+LLKeyboardSDL::LLKeyboardSDL(LLWindowSDL *window)
+	: mWindow(window)
 {
 	// Set up key mapping for SDL - eventually can read this from a file?
 	// Anything not in the key map gets dropped
@@ -44,7 +46,7 @@
 
 	// Virtual key mappings from SDL_keysym.h ...
 
-	// SDL maps the letter keys to the ASCII you'd expect, but it's lowercase...
+	// Gtk/Gdk maps the letter keys to the ASCII.
 	U16 cur_char;
 	for (cur_char = 'A'; cur_char <= 'Z'; cur_char++)
 	{
@@ -73,60 +75,60 @@
 	//mTranslateKeyMap[SDLK_KP3] = KEY_PAGE_DOWN;
 	//mTranslateKeyMap[SDLK_KP0] = KEY_INSERT;
 
-	mTranslateKeyMap[SDLK_SPACE] = ' ';
-	mTranslateKeyMap[SDLK_RETURN] = KEY_RETURN;
-	mTranslateKeyMap[SDLK_LEFT] = KEY_LEFT;
-	mTranslateKeyMap[SDLK_RIGHT] = KEY_RIGHT;
-	mTranslateKeyMap[SDLK_UP] = KEY_UP;
-	mTranslateKeyMap[SDLK_DOWN] = KEY_DOWN;
-	mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE;
-	mTranslateKeyMap[SDLK_KP_ENTER] = KEY_RETURN;
-	mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE;
-	mTranslateKeyMap[SDLK_BACKSPACE] = KEY_BACKSPACE;
-	mTranslateKeyMap[SDLK_DELETE] = KEY_DELETE;
-	mTranslateKeyMap[SDLK_LSHIFT] = KEY_SHIFT;
-	mTranslateKeyMap[SDLK_RSHIFT] = KEY_SHIFT;
-	mTranslateKeyMap[SDLK_LCTRL] = KEY_CONTROL;
-	mTranslateKeyMap[SDLK_RCTRL] = KEY_CONTROL;
-	mTranslateKeyMap[SDLK_LALT] = KEY_ALT;
-	mTranslateKeyMap[SDLK_RALT] = KEY_ALT;
-	mTranslateKeyMap[SDLK_HOME] = KEY_HOME;
-	mTranslateKeyMap[SDLK_END] = KEY_END;
-	mTranslateKeyMap[SDLK_PAGEUP] = KEY_PAGE_UP;
-	mTranslateKeyMap[SDLK_PAGEDOWN] = KEY_PAGE_DOWN;
-	mTranslateKeyMap[SDLK_MINUS] = KEY_HYPHEN;
-	mTranslateKeyMap[SDLK_EQUALS] = KEY_EQUALS;
-	mTranslateKeyMap[SDLK_KP_EQUALS] = KEY_EQUALS;
-	mTranslateKeyMap[SDLK_INSERT] = KEY_INSERT;
-	mTranslateKeyMap[SDLK_CAPSLOCK] = KEY_CAPSLOCK;
-	mTranslateKeyMap[SDLK_TAB] = KEY_TAB;
-	mTranslateKeyMap[SDLK_KP_PLUS] = KEY_ADD;
-	mTranslateKeyMap[SDLK_KP_MINUS] = KEY_SUBTRACT;
-	mTranslateKeyMap[SDLK_KP_MULTIPLY] = KEY_MULTIPLY;
-	mTranslateKeyMap[SDLK_KP_DIVIDE] = KEY_DIVIDE;
-	mTranslateKeyMap[SDLK_F1] = KEY_F1;
-	mTranslateKeyMap[SDLK_F2] = KEY_F2;
-	mTranslateKeyMap[SDLK_F3] = KEY_F3;
-	mTranslateKeyMap[SDLK_F4] = KEY_F4;
-	mTranslateKeyMap[SDLK_F5] = KEY_F5;
-	mTranslateKeyMap[SDLK_F6] = KEY_F6;
-	mTranslateKeyMap[SDLK_F7] = KEY_F7;
-	mTranslateKeyMap[SDLK_F8] = KEY_F8;
-	mTranslateKeyMap[SDLK_F9] = KEY_F9;
-	mTranslateKeyMap[SDLK_F10] = KEY_F10;
-	mTranslateKeyMap[SDLK_F11] = KEY_F11;
-	mTranslateKeyMap[SDLK_F12] = KEY_F12;
-	mTranslateKeyMap[SDLK_PLUS]   = '=';
-	mTranslateKeyMap[SDLK_COMMA]  = ',';
-	mTranslateKeyMap[SDLK_MINUS]  = '-';
-	mTranslateKeyMap[SDLK_PERIOD] = '.';
-	mTranslateKeyMap[SDLK_BACKQUOTE] = '`';
-	mTranslateKeyMap[SDLK_SLASH] = '/';
-	mTranslateKeyMap[SDLK_SEMICOLON] = ';';
-	mTranslateKeyMap[SDLK_LEFTBRACKET] = '[';
-	mTranslateKeyMap[SDLK_BACKSLASH] = '\\';
-	mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']';
-	mTranslateKeyMap[SDLK_QUOTE] = '\'';
+	mTranslateKeyMap[GDK_space] = ' ';
+	mTranslateKeyMap[GDK_Return] = KEY_RETURN;
+	mTranslateKeyMap[GDK_Left] = KEY_LEFT;
+	mTranslateKeyMap[GDK_Right] = KEY_RIGHT;
+	mTranslateKeyMap[GDK_Up] = KEY_UP;
+	mTranslateKeyMap[GDK_Down] = KEY_DOWN;
+	mTranslateKeyMap[GDK_Escape] = KEY_ESCAPE;
+	mTranslateKeyMap[GDK_KP_Enter] = KEY_RETURN;
+	mTranslateKeyMap[GDK_Escape] = KEY_ESCAPE;
+	mTranslateKeyMap[GDK_BackSpace] = KEY_BACKSPACE;
+	mTranslateKeyMap[GDK_Delete] = KEY_DELETE;
+	mTranslateKeyMap[GDK_Shift_L] = KEY_SHIFT;
+	mTranslateKeyMap[GDK_Shift_R] = KEY_SHIFT;
+	mTranslateKeyMap[GDK_Control_L] = KEY_CONTROL;
+	mTranslateKeyMap[GDK_Control_R] = KEY_CONTROL;
+	mTranslateKeyMap[GDK_Alt_L] = KEY_ALT;
+	mTranslateKeyMap[GDK_Alt_R] = KEY_ALT;
+	mTranslateKeyMap[GDK_Home] = KEY_HOME;
+	mTranslateKeyMap[GDK_End] = KEY_END;
+	mTranslateKeyMap[GDK_Page_Up] = KEY_PAGE_UP;
+	mTranslateKeyMap[GDK_Page_Down] = KEY_PAGE_DOWN;
+	mTranslateKeyMap[GDK_minus] = KEY_HYPHEN;
+	mTranslateKeyMap[GDK_equal] = KEY_EQUALS;
+	mTranslateKeyMap[GDK_KP_Equal] = KEY_EQUALS;
+	mTranslateKeyMap[GDK_Insert] = KEY_INSERT;
+	mTranslateKeyMap[GDK_Caps_Lock] = KEY_CAPSLOCK;
+	mTranslateKeyMap[GDK_Tab] = KEY_TAB;
+	mTranslateKeyMap[GDK_KP_Add] = KEY_ADD;
+	mTranslateKeyMap[GDK_KP_Subtract] = KEY_SUBTRACT;
+	mTranslateKeyMap[GDK_KP_Multiply] = KEY_MULTIPLY;
+	mTranslateKeyMap[GDK_KP_Divide] = KEY_DIVIDE;
+	mTranslateKeyMap[GDK_F1] = KEY_F1;
+	mTranslateKeyMap[GDK_F2] = KEY_F2;
+	mTranslateKeyMap[GDK_F3] = KEY_F3;
+	mTranslateKeyMap[GDK_F4] = KEY_F4;
+	mTranslateKeyMap[GDK_F5] = KEY_F5;
+	mTranslateKeyMap[GDK_F6] = KEY_F6;
+	mTranslateKeyMap[GDK_F7] = KEY_F7;
+	mTranslateKeyMap[GDK_F8] = KEY_F8;
+	mTranslateKeyMap[GDK_F9] = KEY_F9;
+	mTranslateKeyMap[GDK_F10] = KEY_F10;
+	mTranslateKeyMap[GDK_F11] = KEY_F11;
+	mTranslateKeyMap[GDK_F12] = KEY_F12;
+	mTranslateKeyMap[GDK_plus]   = '=';
+	mTranslateKeyMap[GDK_comma]  = ',';
+	mTranslateKeyMap[GDK_minus]  = '-';
+	mTranslateKeyMap[GDK_period] = '.';
+	mTranslateKeyMap[GDK_quoteleft] = '`';
+	mTranslateKeyMap[GDK_slash] = '/';
+	mTranslateKeyMap[GDK_semicolon] = ';';
+	mTranslateKeyMap[GDK_bracketleft] = '[';
+	mTranslateKeyMap[GDK_backslash] = '\\';
+	mTranslateKeyMap[GDK_bracketright] = ']';
+	mTranslateKeyMap[GDK_quoteright] = '\'';
 
 	// Build inverse map
 	std::map<U16, KEY>::iterator iter;
@@ -136,17 +138,17 @@
 	}
 
 	// numpad map
-	mTranslateNumpadMap[SDLK_KP0] = KEY_PAD_INS;
-	mTranslateNumpadMap[SDLK_KP1] = KEY_PAD_END;
-	mTranslateNumpadMap[SDLK_KP2] = KEY_PAD_DOWN;
-	mTranslateNumpadMap[SDLK_KP3] = KEY_PAD_PGDN;
-	mTranslateNumpadMap[SDLK_KP4] = KEY_PAD_LEFT;
-	mTranslateNumpadMap[SDLK_KP5] = KEY_PAD_CENTER;
-	mTranslateNumpadMap[SDLK_KP6] = KEY_PAD_RIGHT;
-	mTranslateNumpadMap[SDLK_KP7] = KEY_PAD_HOME;
-	mTranslateNumpadMap[SDLK_KP8] = KEY_PAD_UP;
-	mTranslateNumpadMap[SDLK_KP9] = KEY_PAD_PGUP;
-	mTranslateNumpadMap[SDLK_KP_PERIOD] = KEY_PAD_DEL;
+	mTranslateNumpadMap[GDK_KP_0] = KEY_PAD_INS;
+	mTranslateNumpadMap[GDK_KP_1] = KEY_PAD_END;
+	mTranslateNumpadMap[GDK_KP_2] = KEY_PAD_DOWN;
+	mTranslateNumpadMap[GDK_KP_3] = KEY_PAD_PGDN;
+	mTranslateNumpadMap[GDK_KP_4] = KEY_PAD_LEFT;
+	mTranslateNumpadMap[GDK_KP_5] = KEY_PAD_CENTER;
+	mTranslateNumpadMap[GDK_KP_6] = KEY_PAD_RIGHT;
+	mTranslateNumpadMap[GDK_KP_7] = KEY_PAD_HOME;
+	mTranslateNumpadMap[GDK_KP_8] = KEY_PAD_UP;
+	mTranslateNumpadMap[GDK_KP_9] = KEY_PAD_PGUP;
+	mTranslateNumpadMap[GDK_KP_Decimal] = KEY_PAD_DEL;
 
 	// build inverse numpad map
 	for (iter = mTranslateNumpadMap.begin();
@@ -159,45 +161,47 @@
 
 void LLKeyboardSDL::resetMaskKeys()
 {
-	SDLMod mask = SDL_GetModState();
+	GdkModifierType mask;
+	gdk_display_get_pointer(gtk_widget_get_display(mWindow->getGtkWindow()), NULL, NULL, NULL, &mask);
 
 	// MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys().
 	//    It looks a bit suspicious, as it won't correct for keys that have been released.
 	//    Is this the way it's supposed to work?
 
-	if(mask & KMOD_SHIFT)
+	if(mask & GDK_SHIFT_MASK)
 	{
 		mKeyLevel[KEY_SHIFT] = TRUE;
 	}
 
-	if(mask & KMOD_CTRL)
+	if(mask & GDK_CONTROL_MASK)
 	{
 		mKeyLevel[KEY_CONTROL] = TRUE;
 	}
 
-	if(mask & KMOD_ALT)
+	if(mask & GDK_MOD1_MASK)
 	{
 		mKeyLevel[KEY_ALT] = TRUE;
 	}
 }
 
 
-MASK LLKeyboardSDL::updateModifiers(const U32 mask)
+// Translate the Gtk modifier mask into LL mask.
+MASK LLKeyboardSDL::updateModifiers(const MASK mask)
 {
-	// translate the mask
+	const GdkModifierType in_mask = (GdkModifierType) mask;
 	MASK out_mask = MASK_NONE;
 
-	if(mask & KMOD_SHIFT)
+	if(in_mask & GDK_SHIFT_MASK)
 	{
 		out_mask |= MASK_SHIFT;
 	}
 
-	if(mask & KMOD_CTRL)
+	if(in_mask & GDK_CONTROL_MASK)
 	{
 		out_mask |= MASK_CONTROL;
 	}
 
-	if(mask & KMOD_ALT)
+	if(in_mask & GDK_MOD1_MASK)
 	{
 		out_mask |= MASK_ALT;
 	}
@@ -206,8 +210,9 @@
 }
 
 
-static U16 adjustNativekeyFromUnhandledMask(const U16 key, const U32 mask)
+static U16 adjustNativekeyFromUnhandledMask(const U16 key, const MASK mask)
 {
+#if 0
 	// SDL doesn't automatically adjust the keysym according to
 	// whether NUMLOCK is engaged, so we massage the keysym manually.
 	U16 rtn = key;
@@ -215,23 +220,27 @@
 	{
 		switch (key)
 		{
-		case SDLK_KP_PERIOD: rtn = SDLK_DELETE; break;
-		case SDLK_KP0: rtn = SDLK_INSERT; break;
-		case SDLK_KP1: rtn = SDLK_END; break;
-		case SDLK_KP2: rtn = SDLK_DOWN; break;
-		case SDLK_KP3: rtn = SDLK_PAGEDOWN; break;
-		case SDLK_KP4: rtn = SDLK_LEFT; break;
-		case SDLK_KP6: rtn = SDLK_RIGHT; break;
-		case SDLK_KP7: rtn = SDLK_HOME; break;
-		case SDLK_KP8: rtn = SDLK_UP; break;
-		case SDLK_KP9: rtn = SDLK_PAGEUP; break;
+		case GDK_KP_PERIOD: rtn = GDK_DELETE; break;
+		case GDK_KP0: rtn = GDK_INSERT; break;
+		case GDK_KP1: rtn = GDK_END; break;
+		case GDK_KP2: rtn = GDK_DOWN; break;
+		case GDK_KP3: rtn = GDK_PAGEDOWN; break;
+		case GDK_KP4: rtn = GDK_LEFT; break;
+		case GDK_KP6: rtn = GDK_RIGHT; break;
+		case GDK_KP7: rtn = GDK_HOME; break;
+		case GDK_KP8: rtn = GDK_UP; break;
+		case GDK_KP9: rtn = GDK_PAGEUP; break;
 		}
 	}
 	return rtn;
+#else
+	/* XXX */
+	return key;
+#endif
 }
 
 
-BOOL LLKeyboardSDL::handleKeyDown(const U16 key, const U32 mask)
+BOOL LLKeyboardSDL::handleKeyDown(const U16 key, const MASK mask)
 {
 	U16     adjusted_nativekey;
 	KEY	translated_key = 0;
@@ -251,7 +260,7 @@
 }
 
 
-BOOL LLKeyboardSDL::handleKeyUp(const U16 key, const U32 mask)
+BOOL LLKeyboardSDL::handleKeyUp(const U16 key, const MASK mask)
 {
 	U16     adjusted_nativekey;
 	KEY	translated_key = 0;
@@ -273,16 +282,30 @@
 MASK LLKeyboardSDL::currentMask(BOOL for_mouse_event)
 {
 	MASK result = MASK_NONE;
-	SDLMod mask = SDL_GetModState();
+	GdkModifierType modifiers;
+	gdk_display_get_pointer(gtk_widget_get_display(mWindow->getGtkWindow()), NULL, NULL, NULL, &modifiers);
 
-	if (mask & KMOD_SHIFT)			result |= MASK_SHIFT;
-	if (mask & KMOD_CTRL)			result |= MASK_CONTROL;
-	if (mask & KMOD_ALT)			result |= MASK_ALT;
+	if (modifiers & GDK_SHIFT_MASK)
+	{
+		result |= MASK_SHIFT;
+	}
+	if (modifiers & GDK_CONTROL_MASK)
+	{
+		result |= MASK_CONTROL;
+	}
+	if (modifiers & GDK_MOD1_MASK)
+	{
+		result |= MASK_ALT;
+	}
 
 	// For keyboard events, consider Meta keys equivalent to Control
+	// ... Fine, but which key in Microsoft 109 keyboard is the Meta (MOD2) key?
 	if (!for_mouse_event)
 	{
-		if (mask & KMOD_META) result |= MASK_CONTROL;
+		if (modifiers & GDK_MOD2_MASK)
+		{
+			result |= MASK_CONTROL;
+		}
 	}
 
 	return result;
Index: indra/llwindow/llwindowsdl.cpp
===================================================================
--- indra/llwindow/llwindowsdl.cpp	(revision 81254)
+++ indra/llwindow/llwindowsdl.cpp	(revision 81255)
@@ -1,6 +1,6 @@
 /** 
  * @file llwindowsdl.cpp
- * @brief SDL implementation of LLWindow class
+ * @brief GTK+/GtkGLExt implementation of LLWindow class
  *
  * $LicenseInfo:firstyear=2001&license=viewergpl$
  * 
@@ -29,8 +29,6 @@
  * $/LicenseInfo$
  */
 
-#if LL_SDL
-
 #include "linden_common.h"
 
 #include "llwindowsdl.h"
@@ -44,175 +42,121 @@
 
 #include "indra_constants.h"
 
-#if LL_GTK
-extern "C" {
-# include "gtk/gtk.h"
-}
-#endif // LL_GTK
-
-#if LL_LINUX || LL_SOLARIS
-// not necessarily available on random SDL platforms, so #if LL_LINUX
-// for execv(), waitpid(), fork()
-# include <unistd.h>
-# include <sys/types.h>
-# include <sys/wait.h>
-#endif // LL_LINUX || LL_SOLARIS
-
-extern BOOL gDebugWindowProc;
-
-const S32	MAX_NUM_RESOLUTIONS = 32;
-
+#include "llpreeditor.h"
+  
+#include "gdk/gdk.h"
+#include "gdk/gdkkeysyms.h"
+#include "gtk/gtk.h"
+#include "gtk/gtkgl.h"
+  
+#if LL_XRANDR
+#include "gdk/gdkx.h"
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+#endif
+  
+#if LL_XF86VM
+#include "gdk/gdkx.h"
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#define BOOL XBOOL // Typedef BOOL conflicts on my environment...
+#include <X11/extensions/xf86vmode.h>
+#undef BOOL
+#ifndef MODE_OK
+#define MODE_OK 0
+#endif
+#endif
+  
+#include <vector>
+#include <algorithm>
+ 
 //
 // LLWindowSDL
 //
-
-#if LL_X11
-# include <X11/Xutil.h>
-#endif //LL_X11
-
+  
+static const S32 RECOMMENDED_WINDOW_WIDTH = 800;
+static const S32 RECOMMENDED_WINDOW_HEIGHT = 600;
+  
+static const S32 MINIMUM_FULLSCREEN_WIDTH = 640;
+static const S32 MINIMUM_FULLSCREEN_HEIGHT = 480;
+ 
 // TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for a similar
 // set of reasons): Stash a pointer to the LLWindowSDL object here and
 // maintain in the constructor and destructor.  This assumes that there will
 // be only one object of this class at any time.  Currently this is true.
 static LLWindowSDL *gWindowImplementation = NULL;
-
-static BOOL was_fullscreen = FALSE;
-
-
-void maybe_lock_display(void)
-{
-	if (gWindowImplementation) {
-		gWindowImplementation->Lock_Display();
-	}
-}
-
-
-void maybe_unlock_display(void)
-{
-	if (gWindowImplementation) {
-		gWindowImplementation->Unlock_Display();
-	}
-}
-
-
-#if LL_GTK
+  
 // Lazily initialize and check the runtime GTK version for goodness.
 BOOL ll_try_gtk_init(void)
 {
-	static BOOL done_gtk_diag = FALSE;
-	static BOOL gtk_is_good = FALSE;
-	static BOOL done_setlocale = FALSE;
-	static BOOL tried_gtk_init = FALSE;
-
-	if (!done_setlocale)
+ 	static BOOL tried_gtk_init = FALSE;
+ 	static BOOL gtk_is_good = FALSE;
+  
+ 	if (tried_gtk_init)
 	{
-		llinfos << "Starting GTK Initialization." << llendl;
-		maybe_lock_display();
-		gtk_disable_setlocale();
-		maybe_unlock_display();
-		done_setlocale = TRUE;
+		return gtk_is_good;
 	}
-	
-	if (!tried_gtk_init)
+ 	tried_gtk_init = TRUE;
+  
+ 	llinfos << "Starting GTK Initialization." << llendl;
+  
+ 	// We *need* to issue setlocale to run immodules.
+ 	// gtk_disable_setlocale();
+  
+ 	if (!g_thread_supported())
 	{
-		tried_gtk_init = TRUE;
-#if LL_GSTREAMER_ENABLED
-		if (!g_thread_supported ()) g_thread_init (NULL);
-#endif // LL_GSTREAMER_ENABLED
-		maybe_lock_display();
-		gtk_is_good = gtk_init_check(NULL, NULL);
-		maybe_unlock_display();
-		if (!gtk_is_good)
-			llwarns << "GTK Initialization failed." << llendl;
+		g_thread_init(NULL);
 	}
-
-	if (gtk_is_good && !done_gtk_diag)
+ 
+ 	if (!gtk_init_check(NULL, NULL))
 	{
-		llinfos << "GTK Initialized." << llendl;
-		llinfos << "- Compiled against GTK version "
-			<< GTK_MAJOR_VERSION << "."
-			<< GTK_MINOR_VERSION << "."
-			<< GTK_MICRO_VERSION << llendl;
-		llinfos << "- Running against GTK version "
-			<< gtk_major_version << "."
-			<< gtk_minor_version << "."
-			<< gtk_micro_version << llendl;
-		gchar *gtk_warning;
-		maybe_lock_display();
-		gtk_warning = gtk_check_version(GTK_MAJOR_VERSION,
-						GTK_MINOR_VERSION,
-						GTK_MICRO_VERSION);
-		maybe_unlock_display();
-		if (gtk_warning)
-		{
-			llwarns << "- GTK COMPATIBILITY WARNING: " <<
-				gtk_warning << llendl;
-			gtk_is_good = FALSE;
-		}
-
-		done_gtk_diag = TRUE;
+		llwarns << "GTK Initialization failed." << llendl;
+		// The message box may not appear, since GTK didn't initialize...
+		OSMessageBox("Gtk initialization failure.", "Error", OSMB_OK);
+		return gtk_is_good;
 	}
-
-	return gtk_is_good;
-}
-#endif // LL_GTK
-
-
-#if LL_X11
-Window get_SDL_XWindowID(void)
-{
-	if (gWindowImplementation) {
-		return gWindowImplementation->mSDL_XWindowID;
+  
+ 	llinfos << "GTK Initialized." << llendl;
+ 	llinfos << "- Compiled against GTK version "
+		<< GTK_MAJOR_VERSION << "."
+		<< GTK_MINOR_VERSION << "."
+		<< GTK_MICRO_VERSION << llendl;
+ 	llinfos << "- Running against GTK version "
+		<< gtk_major_version << "."
+		<< gtk_minor_version << "."
+		<< gtk_micro_version << llendl;
+ 	const gchar *gtk_warning;
+ 	gtk_warning = gtk_check_version(GTK_MAJOR_VERSION,
+ 					GTK_MINOR_VERSION,
+ 					GTK_MICRO_VERSION);
+ 	if (gtk_warning)
+	{
+		llwarns << "- GTK COMPATIBILITY WARNING: " << gtk_warning << llendl;
 	}
-	return None;
-}
-
-Display* get_SDL_Display(void)
-{
-	if (gWindowImplementation) {
-		return gWindowImplementation->mSDL_Display;
-	}
-	return NULL;
-}
-#endif // LL_X11
-
-
-BOOL check_for_card(const char* RENDERER, const char* bad_card)
-{
-	if (!strncasecmp(RENDERER, bad_card, strlen(bad_card)))
+	
+ 	if (!gtk_gl_init_check(NULL, NULL))
 	{
-		char buffer[1024];	/* Flawfinder: ignore */
-		snprintf(buffer, sizeof(buffer),	
-			"Your video card appears to be a %s, which Second Life does not support.\n"
-			"\n"
-			"Second Life requires a video card with 32 Mb of memory or more, as well as\n"
-			"multitexture support.  We explicitly support nVidia GeForce 2 or better, \n"
-			"and ATI Radeon 8500 or better.\n"
-			"\n"
-			"If you own a supported card and continue to receive this message, try \n"
-			"updating to the latest video card drivers. Otherwise look in the\n"
-			"secondlife.com support section or e-mail technical support\n"
-			"\n"
-			"You can try to run Second Life, but it will probably crash or run\n"
-			"very slowly.  Try anyway?",
-			bad_card);
-		S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
-		if (OSBTN_YES == button)
-		{
-			return FALSE;
-		}
-		else
-		{
-			return TRUE;
-		}
+		llwarns << "GTK GL Initialization failed." << llendl;
+		OSMessageBox("Gtk GL initialization failure.", "Error", OSMB_OK);
+		return gtk_is_good;
 	}
-
-	return FALSE;
+  
+ 	llinfos << "GTK GL Initialized." << llendl;
+ 	llinfos << "- Compiled against GTK GL version "
+		<< GTKGLEXT_MAJOR_VERSION << "."
+		<< GTKGLEXT_MINOR_VERSION << "."
+		<< GTKGLEXT_MICRO_VERSION << llendl;
+ 	llinfos << "- Running against GTK version "
+		<< gtkglext_major_version << "."
+		<< gtkglext_minor_version << "."
+		<< gtkglext_micro_version << llendl;
+ 
+ 	gtk_is_good = TRUE;
+  	return gtk_is_good;
 }
 
 
-
-
 LLWindowSDL::LLWindowSDL(char *title, S32 x, S32 y, S32 width,
 							   S32 height, U32 flags,
 							   BOOL fullscreen, BOOL clearBg,
@@ -221,52 +165,54 @@
 	: LLWindow(fullscreen, flags), mGamma(1.0f)
 {
 	// Initialize the keyboard
-	gKeyboard = new LLKeyboardSDL();
-	// Note that we can't set up key-repeat until after SDL has init'd video
+	gKeyboard = new LLKeyboardSDL(this);
 
 	// Ignore use_gl for now, only used for drones on PC
 	mWindow = NULL;
-	mCursorDecoupled = FALSE;
-	mCursorLastEventDeltaX = 0;
-	mCursorLastEventDeltaY = 0;
-	mCursorIgnoreNextDelta = FALSE;
-	mNeedsResize = FALSE;
+	mGLWidget = NULL;
 	mOverrideAspectRatio = 0.f;
 	mGrabbyKeyFlags = 0;
-	mReallyCapturedCount = 0;
-	mHaveInputFocus = -1;
-	mIsMinimized = -1;
+	mPreeditor = NULL;
+	mFlashing = FALSE;
+	mWindowIsFocused = FALSE;
+	mIMContextIsFocused = FALSE;
+	mPrimaryClipboardNeedsReset = TRUE;
+	mSupportedResolutions = NULL;
 
-#if LL_X11
-	mSDL_XWindowID = None;
-	mSDL_Display = NULL;
-#endif // LL_X11
-
-#if LL_GTK
 	// We MUST be the first to initialize GTK, i.e. we have to beat
 	// our embedded Mozilla to the punch so that GTK doesn't get badly
 	// initialized with a non-C locale and cause lots of serious random
 	// weirdness.
-	ll_try_gtk_init();
-#endif // LL_GTK
-
-	// Get the original aspect ratio of the main device.
-	mOriginalAspectRatio = 1024.0 / 768.0;  // !!! *FIX: ? //(double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
-
-	if (!title)
-		title = "SDL Window";  // *FIX: (???)
-
-	// Stash the window title
-	mWindowTitle = new char[strlen(title) + 1]; /* Flawfinder: ignore */
-	if(mWindowTitle == NULL)
+	//
+	// Although the above comments (taken from SDL version) says
+	// "random weirdness", the issue is not random.  The problem is
+	// that SL viewer code assumes a decimal point character is always
+	// a period, and passes the american notation of decimal fraction
+	// to/from locale sensitive number formatting functions.  It's not
+	// a Mozilla problem, but an SL viewer problem.
+	// 
+	// ... and, we need to run immodules under the user's locale.
+	// Else, it may not run properly.  In this version, I simply
+	// ignore the above issue.  As a result, this version does not run
+	// under a locale whose decimal point is not a period.  I will
+	// come back this point in a future.
+	// 
+	if (!ll_try_gtk_init())
 	{
-		llerrs << "Memory allocation failure" << llendl;
 		return;
 	}
 
-	strcpy(mWindowTitle, title); /* Flawfinder: ignore */
+#if LL_X11
+	// See if some X11 extensions are available.  Also does some
+	// initialization when they are.
+	setupX11Extensions();
+#endif
+
+	// Save the title, since we need it everytime creating GL context.
+	mWindowTitle = (title ? title : "LLWindow");
+
 	// Create the GL context and set it up for windowed or fullscreen, as appropriate.
-	if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
+	if(createContext(x, y, width, height, fullscreen, disable_vsync))
 	{
 		gGLManager.initGL();
 
@@ -274,31 +220,32 @@
 		initCursors();
 		setCursor( UI_CURSOR_ARROW );
 	}
-
 	stop_glerror();
 
-	// Stash an object pointer for OSMessageBox()
+	// Initialize input method.
+	createIMContext();	
+
 	gWindowImplementation = this;
-
-#if LL_X11
-	mFlashing = FALSE;
-#endif // LL_X11
 }
 
-static SDL_Surface *Load_BMP_Resource(const char *basename)
+// Load a bmp resource file.  No matter what its name suggests, this
+// function can load a lot of file formats other than BMP.
+static GdkPixbuf *load_bmp_resource(const char *basename, const char * purpose)
 {
-	const int PATH_BUFFER_SIZE=1000;
-	char path_buffer[PATH_BUFFER_SIZE];	/* Flawfinder: ignore */
-	
-	// Figure out where our BMP is living on the disk
-	snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",	
-		 gDirUtilp->getAppRODataDir().c_str(),
-		 gDirUtilp->getDirDelimiter().c_str(),
-		 gDirUtilp->getDirDelimiter().c_str(),
-		 basename);
-	path_buffer[PATH_BUFFER_SIZE-1] = '\0';
-	
-	return SDL_LoadBMP(path_buffer);
+	const std::string path = gDirUtilp->getAppRODataDir()
+		+ gDirUtilp->getDirDelimiter()
+		+ "res-sdl"
+		+ gDirUtilp->getDirDelimiter()
+		+ basename;
+
+	GError *error = NULL;
+	GdkPixbuf * const pixbuf = gdk_pixbuf_new_from_file(path.c_str(), &error);
+	if (!pixbuf)
+	{
+		llwarns << "Loading of " << purpose << " BMP resource failed for " << basename << ": " << error->message << llendl;
+		g_error_free(error);
+	}	
+	return pixbuf;
 }
 
 #if LL_X11
@@ -437,279 +384,189 @@
 }
 #endif // LL_X11
 
-BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
+// Bulk define member function wrappers (aka C-C++ callback thunk.)
+#define EVENT_HANDLER_WRAPPER(HANDLER, EVENT) \
+ 	static gboolean HANDLER##_cb(GtkWidget *widget, EVENT *event, gpointer user_data) \
+ 	{ return static_cast<LLWindowSDL *>(user_data)->HANDLER(event); }
+EVENT_HANDLER_WRAPPER(handleGtkMotionNotifyEvent,  GdkEventMotion);
+EVENT_HANDLER_WRAPPER(handleGtkKeyPressEvent,      GdkEventKey);
+EVENT_HANDLER_WRAPPER(handleGtkKeyReleaseEvent,    GdkEventKey);
+EVENT_HANDLER_WRAPPER(handleGtkButtonPressEvent,   GdkEventButton);
+EVENT_HANDLER_WRAPPER(handleGtkButtonReleaseEvent, GdkEventButton);
+EVENT_HANDLER_WRAPPER(handleGtkScrollEvent,        GdkEventScroll);
+EVENT_HANDLER_WRAPPER(handleGtkFocusInEvent,       GdkEventFocus);
+EVENT_HANDLER_WRAPPER(handleGtkFocusOutEvent,      GdkEventFocus);
+EVENT_HANDLER_WRAPPER(handleGtkExposeEvent,        GdkEventExpose);
+EVENT_HANDLER_WRAPPER(handleGtkConfigureEvent,     GdkEventConfigure);
+EVENT_HANDLER_WRAPPER(handleGtkWindowStateEvent,   GdkEventWindowState);
+EVENT_HANDLER_WRAPPER(handleGtkDeleteEvent,        GdkEvent);
+#undef EVENT_HANDLER_WRAPPER
+ 
+BOOL LLWindowSDL::createContext(S32 x, S32 y, S32 width, S32 height, BOOL fullscreen, BOOL disable_vsync)
 {
-	//bool			glneedsinit = false;
-//    const char *gllibname = null;
-
-	llinfos << "createContext, fullscreen=" << fullscreen <<
-	    " size=" << width << "x" << height << llendl;
-
-	// captures don't survive contexts
-	mGrabbyKeyFlags = 0;
-	mReallyCapturedCount = 0;
-	
-	if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ 	llinfos << "createContext, fullscreen=" << fullscreen << " size=" << width << "x" << height << llendl;
+  
+ 	// Create Window and GL widget.
+ 	mWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ 	mGLWidget = gtk_drawing_area_new();
+ 	gtk_container_add(GTK_CONTAINER(mWindow), mGLWidget);
+  
+ 	initializeGamma();
+ 
+ 	// We can only run on a monitor of the default screen.  It is
+ 	// because some of APIs we rely on implicitly assume default
+ 	// screen and monitor.  Moreover, when using XRandR 1.1, we can't
+ 	// support "multiple monitors on a screen" configuration well, and
+ 	// a workaround is simply ignore any secondary or more monitors.
+ 	// Make sure our window will be realized there.  As a side effect,
+ 	// we get the monitor geometory.  We will use it later.
+ 	GdkScreen * screen = gdk_screen_get_default();
+ 	gtk_window_set_screen(GTK_WINDOW(mWindow), screen);
+ 	gint monitor_number = gdk_screen_get_monitor_at_point(screen, x, y);
+ 	GdkRectangle monitor_rectangle;
+ 	gdk_screen_get_monitor_geometry(screen, monitor_number, &monitor_rectangle);
+ 
+ 	// Find the geometory of the primary monitor on the default
+ 	// screen.  Our window will always be there.  At this moment, we
+ 	// can't support multiple monitor and/or multiple screen
+ 	// environment well.
+ 
+ 	// Take care of initial window size.
+ 	mFullscreen = fullscreen;
+ 	if (!mFullscreen)
 	{
-		llinfos << "sdl_init() failed! " << SDL_GetError() << llendl;
-		setupFailure("window creation error", "error", OSMB_OK);
-		return false;
+		// Supply some reasonable default.
+		if (width <= 0)
+		{
+			width = RECOMMENDED_WINDOW_WIDTH;
+		}
+		if (height <= 0)
+		{
+			height = RECOMMENDED_WINDOW_HEIGHT;
+		}
+		
+		// Let the window fit in the monitor we have examined.
+		width = llmin(width, monitor_rectangle.width);
+		height = llmin(height, monitor_rectangle.height);
+		x = llclamp(x, monitor_rectangle.x, monitor_rectangle.x + monitor_rectangle.width - width);
+		y = llclamp(y, monitor_rectangle.y, monitor_rectangle.y + monitor_rectangle.height - height);
+		
+		// allocate the window as calculated above.  Size and position
+		// may further be changed by the Window Manager upon realize,
+		// however.
+		gtk_window_set_default_size(GTK_WINDOW(mWindow), width, height);
+		gtk_window_move(GTK_WINDOW(mWindow), x, y);
 	}
-
-	SDL_version c_sdl_version;
-	SDL_VERSION(&c_sdl_version);
-	llinfos << "Compiled against SDL "
-		<< int(c_sdl_version.major) << "."
-		<< int(c_sdl_version.minor) << "."
-		<< int(c_sdl_version.patch) << llendl;
-	const SDL_version *r_sdl_version;
-	r_sdl_version = SDL_Linked_Version();
-	llinfos << " Running against SDL "
-		<< int(r_sdl_version->major) << "."
-		<< int(r_sdl_version->minor) << "."
-		<< int(r_sdl_version->patch) << llendl;
-
-	const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo( );
-	if (!videoInfo)
+  
+ 	// Setup a GL context.
+ 	GdkGLConfig * gl_config
+ 		= gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_ALPHA | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
+ 	if (!gl_config)
 	{
-		llinfos << "SDL_GetVideoInfo() failed! " << SDL_GetError() << llendl;
+		llwarns << "gdk_gl_config_new_by_mode failed." << llendl;
 		setupFailure("Window creation error", "Error", OSMB_OK);
 		return FALSE;
 	}
-
-	SDL_EnableUNICODE(1);
-	SDL_WM_SetCaption(mWindowTitle, mWindowTitle);
-
-	// Set the application icon.
-	SDL_Surface *bmpsurface;
-	bmpsurface = Load_BMP_Resource("ll_icon.BMP");
-	if (bmpsurface)
+  
+ 	gboolean ok	= gtk_widget_set_gl_capability(mGLWidget, gl_config, NULL, TRUE, GDK_GL_RGBA_TYPE);
+ 	if (!ok)
 	{
-		// This attempts to give a black-keyed mask to the icon.
-		SDL_SetColorKey(bmpsurface,
-				SDL_SRCCOLORKEY,
-				SDL_MapRGB(bmpsurface->format, 0,0,0) );
-		SDL_WM_SetIcon(bmpsurface, NULL);
-		// The SDL examples cheerfully avoid freeing the icon
-		// surface, but I'm betting that's leaky.
-		SDL_FreeSurface(bmpsurface);
-		bmpsurface = NULL;
+		llwarns << "gtk_widget_set_gl_capability failed." << llendl;
+		setupFailure("Window creation error", "Error", OSMB_OK);
+		return FALSE;
 	}
-
-	// note: these SetAttributes make Tom's 9600-on-AMD64 fail to
-	// get a visual, but it's broken anyway when it does, and without
-	// these SetAttributes we might easily get an avoidable substandard
-	// visual to work with on most other machines.
-	SDL_GL_SetAttribute(SDL_GL_RED_SIZE,  8);
-	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
-	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
-#if !LL_SOLARIS
-        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24);
-	// We need stencil support for a few (minor) things.
-	if (!getenv("LL_GL_NO_STENCIL"))
-		SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
-#else
-	// NOTE- use smaller Z-buffer to enable more graphics cards
-        //     - This should not affect better GPUs and has been proven
-        //	 to provide 24-bit z-buffers when available.
-	//
-        // As the API states: 
-	//
-        // GLX_DEPTH_SIZE    Must be followed by a nonnegative
-        //                   minimum size specification.  If this
-        //                   value is zero, visuals with no depth
-        //                   buffer are preferred.  Otherwise, the
-        //                   largest available depth buffer of at
-        //                   least the minimum size is preferred.
-
-        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
-#endif
-        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
-
-        // *FIX: try to toggle vsync here?
-
-	mFullscreen = fullscreen;
-	was_fullscreen = fullscreen;
-
-	int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
-
-	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-
-    	mSDLFlags = sdlflags;
-
-	if (mFullscreen)
+  
+ 	// Set the application icon and title.
+ 	GdkPixbuf *icon_pixbuf = load_bmp_resource("ll_icon.BMP", "icon");
+ 	if (icon_pixbuf)
 	{
-		llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
-
-		// If the requested width or height is 0, find the best default for the monitor.
-		if((width == 0) || (height == 0))
+		// Second Life icon is keyed with black.
+		GdkPixbuf * pixbuf = gdk_pixbuf_add_alpha(icon_pixbuf, TRUE, 0, 0, 0);
+		if (pixbuf)
 		{
-			// Scan through the list of modes, looking for one which has:
-			//		height between 700 and 800
-			//		aspect ratio closest to the user's original mode
-			S32 resolutionCount = 0;
-			LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
-
-			if(resolutionList != NULL)
-			{
-				F32 closestAspect = 0;
-				U32 closestHeight = 0;
-				U32 closestWidth = 0;
-				int i;
-
-				llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
-
-				for(i=0; i < resolutionCount; i++)
-				{
-					F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
-
-					llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
-
-					if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
-						(fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
-					{
-						llinfos << " (new closest mode) " << llendl;
-
-						// This is the closest mode we've seen yet.
-						closestWidth = resolutionList[i].mWidth;
-						closestHeight = resolutionList[i].mHeight;
-						closestAspect = aspect;
-					}
-				}
-
-				width = closestWidth;
-				height = closestHeight;
-			}
+			gtk_window_set_icon(GTK_WINDOW(mWindow), pixbuf);
+			g_object_unref(pixbuf);
 		}
-
-		if((width == 0) || (height == 0))
-		{
-			// Mode search failed for some reason.  Use the old-school default.
-			width = 1024;
-			height = 768;
-		}
-
-		mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
-		if (!mWindow && bits > 16)
-		{
-			SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
-			mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
-		}
-
-		if (mWindow)
-		{
-			mFullscreen = TRUE;
-			was_fullscreen = TRUE;
-			mFullscreenWidth   = mWindow->w;
-			mFullscreenHeight  = mWindow->h;
-			mFullscreenBits    = mWindow->format->BitsPerPixel;
-			mFullscreenRefresh = -1;
-
-			llinfos << "Running at " << mFullscreenWidth
-				<< "x"   << mFullscreenHeight
-				<< "x"   << mFullscreenBits
-				<< " @ " << mFullscreenRefresh
-				<< llendl;
-		}
-		else
-		{
-			llwarns << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << llendl;
-			// No fullscreen support
-			mFullscreen = FALSE;
-			was_fullscreen = FALSE;
-			mFullscreenWidth   = -1;
-			mFullscreenHeight  = -1;
-			mFullscreenBits    = -1;
-			mFullscreenRefresh = -1;
-
-			char error[256];	/* Flawfinder: ignore */
-			snprintf(error, sizeof(error), "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);	
-			OSMessageBox(error, "Error", OSMB_OK);
-		}
+		g_object_unref(icon_pixbuf);
 	}
-
-	if(!mFullscreen && (mWindow == NULL))
+ 	gtk_window_set_title(GTK_WINDOW(mWindow), mWindowTitle.c_str());
+  
+ 	// An aesthetic workaround on window resizing and fullscreen
+ 	// switching.  This setting makes newly exposed area in viewer
+ 	// window to be automatically erased with black (before viewer
+ 	// application redraws.)  I think this behaviour looks more
+ 	// natural than leaving those area undrawn or erasing with the
+ 	// GTK's default background color.
+ 	static const GdkColor black = { 0, 0, 0, 0 };
+ 	gtk_widget_modify_bg(mWindow, GTK_STATE_NORMAL, &black);
+  
+ 	// We need to show (map/realize in fact) the GL widget before
+ 	// using GL functions.
+ 	gtk_widget_show_all(mWindow);
+  
+ 	// Take care of full screen resolutions.
+  
+ 	if (mFullscreen)
 	{
-		if (width == 0)
-		    width = 1024;
-		if (height == 0)
-		    width = 768;
-
-		llinfos << "createContext: creating window " << width << "x" << height << "x" << bits << llendl;
-		mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
-		if (!mWindow && bits > 16)
-		{
-			SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
-			mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
-		}
-
-		if (!mWindow)
-		{
-			llwarns << "createContext: window creation failure. SDL: " << SDL_GetError() << llendl;
-			setupFailure("Window creation error", "Error", OSMB_OK);
-			return FALSE;
-		}
-	} else if (!mFullscreen && (mWindow != NULL))
-	{
-		llinfos << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << llendl;
+		// XXX do something to support initial fullscreen...  SL
+		// viewer currently doesn't need this feature, so just ignore
+		// the case.
+		llerrs << "Creating an initlally fullscreen window is not supported." << llendl;
 	}
-	
-	// Detect video memory size.
-# if LL_X11
-	gGLManager.mVRAM = x11_detect_VRAM_kb() / 1024;
-	if (gGLManager.mVRAM != 0)
+  
+  	// Detect video memory size.
+ 	// If VRAM is not detected, that is handled later
+#if LL_X11
+  	gGLManager.mVRAM = x11_detect_VRAM_kb() / 1024;
+  	if (gGLManager.mVRAM != 0)
 	{
 		llinfos << "X11 log-parser detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
-	} else
-# endif // LL_X11
-	{
-		// fallback to letting SDL detect VRAM.
-		// note: I've not seen SDL's detection ever actually find
-		// VRAM != 0, but if SDL *does* detect it then that's a bonus.
-		gGLManager.mVRAM = videoInfo->video_mem / 1024;
-		if (gGLManager.mVRAM != 0)
-		{
-			llinfos << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
-		}
 	}
-	// If VRAM is not detected, that is handled later
-
-	// *TODO: Now would be an appropriate time to check for some
-	// explicitly unsupported cards.
-	//const char* RENDERER = (const char*) glGetString(GL_RENDERER);
-
-	GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits;
-
-	glGetIntegerv(GL_RED_BITS, &redBits);
-	glGetIntegerv(GL_GREEN_BITS, &greenBits);
-	glGetIntegerv(GL_BLUE_BITS, &blueBits);
-	glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
-	glGetIntegerv(GL_DEPTH_BITS, &depthBits);
-	glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
-	
-	llinfos << "GL buffer:" << llendl
-        llinfos << "  Red Bits " << S32(redBits) << llendl
-        llinfos << "  Green Bits " << S32(greenBits) << llendl
-        llinfos << "  Blue Bits " << S32(blueBits) << llendl
-	llinfos	<< "  Alpha Bits " << S32(alphaBits) << llendl
-	llinfos	<< "  Depth Bits " << S32(depthBits) << llendl
-	llinfos	<< "  Stencil Bits " << S32(stencilBits) << llendl;
-
-	GLint colorBits = redBits + greenBits + blueBits + alphaBits;
-	// fixme: actually, it's REALLY important for picking that we get at
-	// least 8 bits each of red,green,blue.  Alpha we can be a bit more
-	// relaxed about if we have to.
-#if LL_SOLARIS
-#error && defined(__sparc)
-	if(colorBits < 24)		//HACK:  on SPARC allow 24-bit color
 #else
-	if (colorBits < 32)
+ 	gGLManager.mVRAM = 0;
+#endif // LL_X11
+  
+  	// *TODO: Now would be an appropriate time to check for some
+  	// explicitly unsupported cards.
+  	//const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+  
+ 	GLint depth_bits, stencil_bits, red_bits, green_bits, blue_bits, alpha_bits;
+  
+ 	GdkGLContext * const gl_context = gtk_widget_get_gl_context(mGLWidget);
+ 	GdkGLDrawable * const gl_drawable = gtk_widget_get_gl_drawable(mGLWidget);
+ 	gdk_gl_drawable_make_current(gl_drawable, gl_context);
+ 
+ 	glGetIntegerv(GL_RED_BITS,     &red_bits);
+ 	glGetIntegerv(GL_GREEN_BITS,   &green_bits);
+ 	glGetIntegerv(GL_BLUE_BITS,    &blue_bits);
+ 	glGetIntegerv(GL_ALPHA_BITS,   &alpha_bits);
+ 	glGetIntegerv(GL_DEPTH_BITS,   &depth_bits);
+ 	glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+  	
+ 	llinfos << "GL buffer:" << llendl;
+ 	llinfos << "  Red Bits " << S32(red_bits) << llendl;
+	llinfos << "  Green Bits " << S32(green_bits) << llendl;
+	llinfos << "  Blue Bits " << S32(blue_bits) << llendl;
+ 	llinfos	<< "  Alpha Bits " << S32(alpha_bits) << llendl;
+ 	llinfos	<< "  Depth Bits " << S32(depth_bits) << llendl;
+ 	llinfos	<< "  Stencil Bits " << S32(stencil_bits) << llendl;
+ 
+ 	const GLint color_bits = red_bits + green_bits + blue_bits + alpha_bits;
+  	// fixme: actually, it's REALLY important for picking that we get at
+ 	// least 8 bits each of red, green, blue.  Alpha we can be a bit more
+  	// relaxed about if we have to.
+ 
+#if LL_SOLARIS && defined(__sparc)
+ 	const S32 minimum_color_bits = 24;		//HACK:  on SPARC allow 24-bit color // Why is SPARC CPU architecture special?  -- Alissa
+#else
+ 	const S32 minimum_color_bits = 32;
 #endif
-	{
-		close();
-		setupFailure(
-#if LL_SOLARIS
-#error && defined(__sparc)
+ 
+ 	if (color_bits < minimum_color_bits)
+		{
+			close();
+			setupFailure(
+#if LL_SOLARIS && defined(__sparc)
 			"Second Life requires at least 24-bit color on SPARC to run in a window.\n"
 			"Please use fbconfig to set your default color depth to 24 bits.\n"
 			"You may also need to adjust the X11 setting in SMF.  To do so use\n"
@@ -727,7 +584,7 @@
 	}
 
 #if 0  // *FIX: we're going to brave it for now...
-	if (alphaBits < 8)
+	if (alpha_bits < 8)
 	{
 		close();
 		setupFailure(
@@ -743,211 +600,576 @@
 	}
 #endif
 
-#if LL_X11
-	init_x11clipboard();
-#endif // LL_X11
-	
-	// We need to do this here, once video is init'd
-	if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
-				      SDL_DEFAULT_REPEAT_INTERVAL))
-	    llwarns << "Couldn't enable key-repeat: " << SDL_GetError() <<llendl;
+	g_signal_connect(G_OBJECT(mWindow), "motion-notify-event",  G_CALLBACK(handleGtkMotionNotifyEvent_cb),  this);
+	g_signal_connect(G_OBJECT(mWindow), "key-press-event",      G_CALLBACK(handleGtkKeyPressEvent_cb),      this);
+	g_signal_connect(G_OBJECT(mWindow), "key-release-event",    G_CALLBACK(handleGtkKeyReleaseEvent_cb),    this);
+	g_signal_connect(G_OBJECT(mWindow), "button-press-event",   G_CALLBACK(handleGtkButtonPressEvent_cb),   this);
+	g_signal_connect(G_OBJECT(mWindow), "button-release-event", G_CALLBACK(handleGtkButtonReleaseEvent_cb), this);
+	g_signal_connect(G_OBJECT(mWindow), "scroll-event",         G_CALLBACK(handleGtkScrollEvent_cb),        this);
+	g_signal_connect(G_OBJECT(mWindow), "focus-in-event",       G_CALLBACK(handleGtkFocusInEvent_cb),       this);
+	g_signal_connect(G_OBJECT(mWindow), "focus-out-event",      G_CALLBACK(handleGtkFocusOutEvent_cb),      this);
+	g_signal_connect(G_OBJECT(mWindow), "expose-event",         G_CALLBACK(handleGtkExposeEvent_cb),        this);
+	g_signal_connect(G_OBJECT(mWindow), "configure-event",      G_CALLBACK(handleGtkConfigureEvent_cb),     this);
+	g_signal_connect(G_OBJECT(mWindow), "window-state-event",   G_CALLBACK(handleGtkWindowStateEvent_cb),   this);
+	g_signal_connect(G_OBJECT(mWindow), "delete-event",         G_CALLBACK(handleGtkDeleteEvent_cb),        this);
 
+	gtk_widget_add_events(mWindow,
+		GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | 
+		GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK | 
+		GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK);
+
+	// This call seems redundant...
+	gdk_gl_drawable_gl_begin(gl_drawable, gl_context);
+
 	// Don't need to get the current gamma, since there's a call that restores it to the system defaults.
 	return TRUE;
 }
 
+#if LL_X11
 
-// changing fullscreen resolution, or switching between windowed and fullscreen mode.
-BOOL LLWindowSDL::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+void LLWindowSDL::setupX11Extensions()
 {
-	const BOOL needsRebuild = TRUE;  // Just nuke the context and start over.
-	BOOL result = true;
+#if LL_XRANDR || LL_XF86VM
+	Display * const x11_display = GDK_DISPLAY();
+	int event_base, error_base;
+	int major, minor;
+#endif
 
-	llinfos << "switchContext, fullscreen=" << fullscreen << llendl;
-	stop_glerror();
-	if(needsRebuild)
+	mXRRVersion = 0;
+#if LL_XRANDR
+	if (XRRQueryExtension(x11_display, &event_base, &error_base) &&
+		XRRQueryVersion(x11_display, &major, &minor))
 	{
-		destroyContext();
-		result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
-		if (result)
-		{
-			gGLManager.initGL();
+		llinfos << "XRandR Extension available.  Version "
+				<< major << "." << minor << llendl;
+		mXRRVersion = major * 10 + (minor > 9 ? 9 : minor);
+	}
+	else
+	{
+		llwarns << "XRandR Extension not available." << llendl;
+	}
+#endif
 
-			//start with arrow cursor
-			initCursors();
-			setCursor( UI_CURSOR_ARROW );
-		}
+	mXF86VMVersion = 0;
+#if LL_XF86VM
+	if (XF86VidModeQueryExtension(x11_display, &event_base, &error_base) &&
+		XF86VidModeQueryVersion(x11_display, &major, &minor))
+	{
+		llinfos << "XF86VideoMode Extension available.  Version "
+				<< major << "." << minor << llendl;
+		mXF86VMVersion = major * 10 + (minor > 9 ? 9 : minor);
 	}
+	else
+	{
+		llwarns << "XF86VideoMode Extension not available." << llendl;
+	}
+#endif
+}
 
-	stop_glerror();
+#endif
 
-	return result;
-}
-
 void LLWindowSDL::destroyContext()
 {
-	llinfos << "destroyContext begins" << llendl;
-#if LL_X11
-	quit_x11clipboard();
-#endif // LL_X11
+	if (mWindow)
+	{
+		restoreGamma();
+		if (mFullscreen)
+		{
+			leaveFullscreenResolution();
+		}
 
-	// Clean up remaining GL state before blowing away window
-	llinfos << "shutdownGL begins" << llendl;
-	gGLManager.shutdownGL();
-	llinfos << "SDL_QuitSS/VID begins" << llendl;
-	SDL_QuitSubSystem(SDL_INIT_VIDEO);  // *FIX: this might be risky...
-	//unload_all_glsyms();
+		// Clean up remaining GL state before blowing away window
+		llinfos << "shutdownGL begins" << llendl;
+		gGLManager.shutdownGL();
+		gdk_gl_drawable_gl_end(gtk_widget_get_gl_drawable(mGLWidget));
+		// gtk_widget_destroy(mGLWidget);
 
-	mWindow = NULL;
+		gtk_widget_destroy(mWindow);
+
+		mGLWidget = NULL;
+		mWindow = NULL;
+	}
 }
 
 LLWindowSDL::~LLWindowSDL()
 {
 	quitCursors();
+	destroyIMContext();
 	destroyContext();
 
-	if(mSupportedResolutions != NULL)
+	delete [] mSupportedResolutions;
+	mSupportedResolutions = NULL;
+
+	if (this == gWindowImplementation)
 	{
-		delete []mSupportedResolutions;
+		gWindowImplementation = NULL;
 	}
+}
 
-	delete[] mWindowTitle;
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowSDL::close()
+{
+	// Is window is already closed?
+	//	if (!mWindow)
+	//	{
+	//		return;
+	//	}
 
-	gWindowImplementation = NULL;
+	// Make sure cursor is visible and we haven't mangled the clipping state.
+	setMouseClipping(FALSE);
+	showCursor();
+
+	destroyContext();
 }
 
+// Note on resolution handling:
+// 
+// If we are running on X11 and XRandR is available, use it.  We could
+// use per CRTC resolution control of XRandR 1.2, but it is not
+// implemented on many servers/drivers yet.  We stick on the good old
+// XRandR 1.0.  As an alternative, we can use XF86VideoMode.
 
-void LLWindowSDL::show()
+// We need some support methods with LLWindow::LLWindowResolution so
+// that STL works well with it.  However, it is declared in the
+// LLWindow and used in newview app.  I have a feeling that I should
+// not change it.  As an alternative, we define our own subclass and
+// use it internally.
+
+struct LLResolution : LLWindow::LLWindowResolution
 {
-    // *FIX: What to do with SDL?
+	inline LLResolution()
+	{
+	}
+
+	inline LLResolution(const GdkRectangle &rectangle)
+	{
+		mWidth = rectangle.x;
+		mHeight = rectangle.y;
+	}
+
+#if LL_XRANDR
+	inline LLResolution(const XRRScreenSize &screen_size)
+	{
+		mWidth = screen_size.width;
+		mHeight = screen_size.height;
+	}
+#endif
+
+#if LL_XF86VM
+	inline LLResolution(const XF86VidModeModeInfo *info)
+	{
+		mWidth = info->hdisplay;
+		mHeight = info->vdisplay;
+	}
+#endif
+
+	inline bool operator==(const LLResolution &another) const
+	{
+		return mWidth == another.mWidth && mHeight == another.mHeight;
+	}
+
+	inline bool operator<(const LLResolution &another) const
+	{
+		return mWidth < another.mWidth || (mWidth == another.mWidth && mHeight < another.mHeight);
+	}
+
+	static inline bool too_small(const LLResolution &a)
+	{
+		return a.mWidth < MINIMUM_FULLSCREEN_WIDTH || a.mHeight < MINIMUM_FULLSCREEN_HEIGHT;
+	}
+};
+
+// Given a list of resolutions (as a vector of LLReslution) and a pair
+// of width and height, return the index in the array to the _best_
+// matching resolution.  The best one is either: if there are some
+// resolutions that entirely cover the given width-height pare, the
+// smallest one in the covering resolutions is the best; otherwise the
+// largest one is the best.  In case of a tie, one having the smaller
+// index is considered best in bests.
+static int find_best_resolution(const std::vector<LLResolution> &resolutions,
+								const S32 width, const S32 height)
+{
+	int winner;
+	S32 winner_area;
+
+	winner = -1;
+	winner_area = S32_MAX;
+	for (size_t i = 0; i < resolutions.size(); i++)
+	{
+		const LLResolution r = resolutions[i];
+		const S32 area = r.mWidth * r.mHeight;
+		if (r.mWidth >= width && r.mHeight >= height && area < winner_area)
+		{
+			winner = i;
+			winner_area = area;
+		}
+	}
+	if (winner >= 0)
+	{
+		return winner;
+	}
+
+	winner = 0;
+	winner_area = 0;
+	for (size_t i = 0; i < resolutions.size(); i++)
+	{
+		const LLResolution r = resolutions[i];
+		const S32 area = r.mWidth * r.mHeight;
+		if (area > winner_area)
+		{
+			winner = i;
+			winner_area = area;
+		}
+	}
+	return winner;
 }
 
-void LLWindowSDL::hide()
+// Get the current pixel width and height of the monitor that this
+// window is on.  The width and height are stored at the location
+// where pointers width and height point to.
+void LLWindowSDL::getMonitorResolution(S32 *width, S32 *height)
 {
-    // *FIX: What to do with SDL?
+	GdkScreen * screen = gtk_window_get_screen(GTK_WINDOW(mWindow));
+	gint monitor_number = gdk_screen_get_monitor_at_window(screen, mWindow->window);
+	GdkRectangle monitor_rectangle;
+	gdk_screen_get_monitor_geometry(screen, monitor_number, &monitor_rectangle);
+	*width = monitor_rectangle.width;
+	*height = monitor_rectangle.height;
 }
 
-void LLWindowSDL::minimize()
+// Try to switch the monitor resolution.  width and height are in
+// pixels.  The monitor the window is on is affected.  If the
+// specified width-height combination is not supported, the best
+// matching supported dimension is chosen.
+void LLWindowSDL::switchMonitorResolution(S32 width, S32 height)
 {
-    // *FIX: What to do with SDL?
+#if LL_XRANDR
+	// XRandR 1.2 based per CRTC configuration doesn't work well yet
+	// (on my NVIDIA 100.14.19 server at least), so we will use XRandR
+	// 1.0 compatible method.
+	if (mXRRVersion >= 10)
+	{
+		GdkScreen * const screen = gtk_window_get_screen(GTK_WINDOW(mWindow));
+		Display * const x11_display = GDK_SCREEN_XDISPLAY(screen);
+		const Window x11_root_window = RootWindow(x11_display, GDK_SCREEN_XNUMBER(screen));
+		XRRScreenConfiguration * const screen_config = XRRGetScreenInfo(x11_display, x11_root_window);
+		int nsizes = 0;
+		const XRRScreenSize * const size_list = XRRConfigSizes(screen_config, &nsizes);
+
+		// Look for a best matching resolution.
+		std::vector<LLResolution> resolutions(size_list, size_list + nsizes);
+		const int best = find_best_resolution(resolutions, width, height);
+
+		// Actually switch the monitor resolution.
+		Rotation rotation;
+		XRRConfigRotations(screen_config, &rotation);
+		Time timestamp;
+		XRRConfigTimes(screen_config, &timestamp);
+		const Status status = XRRSetScreenConfig(x11_display, screen_config, x11_root_window,
+												 best, rotation, timestamp);
+		XRRFreeScreenConfigInfo(screen_config);
+
+		if (RRSetConfigSuccess == status)
+		{
+			return;
+		}
+	}
+#endif
+
+#if LL_XF86VM
+	if (mXF86VMVersion >= 10)
+	{
+		GdkScreen * screen = gtk_window_get_screen(GTK_WINDOW(mWindow));
+		Display * x11_display = GDK_SCREEN_XDISPLAY(screen);
+		const int x11_screen_number = GDK_SCREEN_XNUMBER(screen);
+
+		BOOL ok = FALSE;
+
+		XF86VidModeModeInfo **mode_info_array = NULL;
+		int nmodes;
+		if (XF86VidModeGetAllModeLines(x11_display, x11_screen_number, &nmodes, &mode_info_array))
+		{
+			// Find a best matching mode line and switch to it.
+			const std::vector<LLResolution> resolutions(mode_info_array, mode_info_array + nmodes);
+			const int best = find_best_resolution(resolutions, width, height);
+			ok = XF86VidModeSwitchToMode(x11_display, x11_screen_number, mode_info_array[best]);
+			if (ok)
+			{
+				// Although the origin of XF86VM coordinate is at the
+				// top-left, that of GL is at the bottom-left.  We
+				// need to compensate the top-left cooridnate of the
+				// viewport against the screen dimension.  Raw X11
+				// screen dimenstion is used here in case it is
+				// different from GTK's.  (Although I don't know
+				// whether it can.)
+				const int screen_height = DisplayHeight(x11_display, x11_screen_number);
+				const int vertical_offset = screen_height - mode_info_array[best]->vdisplay;
+				XF86VidModeSetViewPort(x11_display, x11_screen_number, 0, vertical_offset);
+			}
+		}
+		if (mode_info_array)
+		{
+			XFree(mode_info_array);
+		}
+
+		if (ok)
+		{
+			return;
+		}
+	}
+#endif
 }
 
-void LLWindowSDL::restore()
+LLWindow::LLWindowResolution * LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
 {
-    // *FIX: What to do with SDL?
+	// We rebuild the list of supported resolutions everytime this
+	// function is called, because (1) it is called infrequently so
+	// the overhead doesn't matter, and (2) under the multiple monitor
+	// environment, the application window may have been moved to a
+	// different monitor that supports different set of resolutions.
+	// When a method returned a singleton, we skip to the next method,
+	// since the reported resolution is very likely a synthetic.
+	std::vector<LLResolution> resolutions;
+
+#if LL_XRANDR
+	if (2 > resolutions.size() && 10 <= mXRRVersion)
+	{
+		GdkScreen *screen = gtk_widget_get_screen(mWindow);
+		Display * x11_display = GDK_SCREEN_XDISPLAY(screen);
+		Window x11_root_window = RootWindow(x11_display, GDK_SCREEN_XNUMBER(screen));
+		XRRScreenConfiguration *screen_config = XRRGetScreenInfo(x11_display, x11_root_window);
+		int nsizes = 0;
+		XRRScreenSize * size_list = XRRConfigSizes(screen_config, &nsizes);
+		if (size_list && nsizes > 0)
+		{
+			resolutions.assign(size_list, size_list + nsizes);
+		}
+		XRRFreeScreenConfigInfo(screen_config);
+	}
+#endif
+
+#if LL_XF86VM
+	if (2 > resolutions.size() && 20 <= mXF86VMVersion)
+	{
+		GdkScreen * screen = gtk_window_get_screen(GTK_WINDOW(mWindow));
+		Display * x11_display = GDK_SCREEN_XDISPLAY(screen);
+		int x11_screen_number = GDK_SCREEN_XNUMBER(screen);
+		XF86VidModeModeInfo **mode_info_array;
+		int nmodes = 0;
+		if (XF86VidModeGetAllModeLines(x11_display, x11_screen_number, &nmodes, &mode_info_array)
+			&& nmodes > 0)
+		{
+			// I think we should filter the returned list with
+			// XF86VidModeValidateModeLine() before copying to
+			// resolutions.  However, the function seems *always* to
+			// return non zero value if run with ATI/AMD vide cards...
+			resolutions.assign(mode_info_array, mode_info_array + nmodes);
+		}
+		XFree(mode_info_array);
+	}
+#endif
+
+	// Filter the unnecessary and/or unusable resolutions.  We also
+	// need to remove duplicates, since a list of XF86 mode lines
+	// often contain timing variations for a same width-height
+	// combination.
+	std::vector<LLResolution>::iterator end = resolutions.end();
+	end = std::remove_if(resolutions.begin(), end, LLResolution::too_small);
+	std::sort(resolutions.begin(), end);
+	end = std::unique(resolutions.begin(), end);
+	resolutions.erase(end, resolutions.end());
+
+	// If any of the above doesn't work, or we got no meaningful list,
+	// we consider that we can't switch the monitor resolution.
+	// Report the current GTK monitor dimension as the only fullscreen
+	// resolution.
+	if (2 > resolutions.size())
+	{
+		GdkScreen * screen = gtk_window_get_screen(GTK_WINDOW(mWindow));
+		gint monitor_number = gdk_screen_get_monitor_at_window(screen, mWindow->window);
+		GdkRectangle monitor_rectangle;
+		gdk_screen_get_monitor_geometry(screen, monitor_number, &monitor_rectangle);
+		resolutions.assign(1, monitor_rectangle);
+	}
+			
+	// I'm not sure we need to maintain mNumSupportedResolutions and
+	// mSupportedResolutions, but it seems safe to do so.
+	delete[] mSupportedResolutions;
+	mSupportedResolutions = new LLWindowResolution[resolutions.size()];
+	std::copy(resolutions.begin(), resolutions.end(), mSupportedResolutions);
+	mNumSupportedResolutions = resolutions.size();
+
+	num_resolutions = mNumSupportedResolutions;
+	return mSupportedResolutions;
 }
 
-
-// close() destroys all OS-specific code associated with a window.
-// Usually called from LLWindowManager::destroyWindow()
-void LLWindowSDL::close()
+// Change fullscreen resolution and/or switch between windowed and fullscreen mode.
+BOOL LLWindowSDL::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
 {
-	// Is window is already closed?
-	//	if (!mWindow)
-	//	{
-	//		return;
-	//	}
+	if (mFullscreen && fullscreen)
+	{
+		// Change fullscreen resolution.
+		mFullscreenWidth = size.mX;
+		mFullscreenHeight = size.mY;
+		switchMonitorResolution(size.mX, size.mY);
+	}
+	else if (mFullscreen && !fullscreen)
+	{
+		// Switch from fullscreen to windowed.
+		switchMonitorResolution(mOriginalMonitorWidth, mOriginalMonitorHeight);
+		gtk_window_unfullscreen(GTK_WINDOW(mWindow));
+	}
+	else if (!mFullscreen && fullscreen)
+	{
+		// Switch from windowed to fullscreen.
+		getMonitorResolution(&mOriginalMonitorWidth, &mOriginalMonitorHeight);
+		gtk_window_fullscreen(GTK_WINDOW(mWindow));
+		mFullscreenWidth = size.mX;
+		mFullscreenHeight = size.mY;
+		switchMonitorResolution(size.mX, size.mY);
+	}
+	else
+	{
+		// Switch from windowed to windowed...  Do nothing anyway.
+	}
 
-	// Make sure cursor is visible and we haven't mangled the clipping state.
-	setMouseClipping(FALSE);
-	showCursor();
+	mFullscreen = fullscreen;
 
-	destroyContext();
+	// We always return TRUE, since under some configuration it can be
+	// impossible to fulfill the requested resolution change, and
+	// success/failure of fullscreen/unfullscreen changes is
+	// undetectable under GTK.  (If we are running on GNOME desktop,
+	// it always succeeds.).
+	return TRUE;
 }
 
+// Make monitor resolutioin suitable for fullscreen operation.
+void LLWindowSDL::enterFullscreenResolution()
+{
+	getMonitorResolution(&mOriginalMonitorWidth, &mOriginalMonitorHeight);
+	switchMonitorResolution(mFullscreenWidth, mFullscreenHeight);
+}
+
+// Make monitor resolution suitable for windowed operation, i.e., the
+// original desktop resolution.
+void LLWindowSDL::leaveFullscreenResolution()
+{
+	switchMonitorResolution(mOriginalMonitorWidth, mOriginalMonitorHeight);
+}
+
 BOOL LLWindowSDL::isValid()
 {
 	return (mWindow != NULL);
 }
 
-BOOL LLWindowSDL::getVisible()
+void LLWindowSDL::show()
 {
-	BOOL result = FALSE;
+	gtk_window_present(GTK_WINDOW(mWindow));
+}
 
-    // *FIX: This isn't really right...
-	// Then what is?
-	if (mWindow)
-	{
-		result = TRUE;
-	}
-
-	return(result);
+void LLWindowSDL::hide()
+{
+	setMouseClipping(FALSE);
+	gtk_widget_hide(mWindow);
 }
 
-BOOL LLWindowSDL::getMinimized()
+void LLWindowSDL::minimize()
 {
-	BOOL result = FALSE;
-
-	if (mWindow && (1 == mIsMinimized))
+	setMouseClipping(FALSE);
+	if (mFullscreen)
 	{
-		result = TRUE;
+		leaveFullscreenResolution();
 	}
-	return(result);
+	gtk_window_iconify(GTK_WINDOW(mWindow));
 }
 
-BOOL LLWindowSDL::getMaximized()
+BOOL LLWindowSDL::maximize()
 {
-	BOOL result = FALSE;
+	setMouseClipping(FALSE);
+	gtk_window_maximize(GTK_WINDOW(mWindow));
+	return 0 != (gdk_window_get_state(mWindow->window) & GDK_WINDOW_STATE_MAXIMIZED);
+}
 
-	if (mWindow)
+void LLWindowSDL::restore()
+{
+	const GdkWindowState state = gdk_window_get_state(mWindow->window);
+	if (state & GDK_WINDOW_STATE_ICONIFIED)
 	{
-		// TODO
+		gtk_window_deiconify(GTK_WINDOW(mWindow));
+		if (mFullscreen)
+		{
+			enterFullscreenResolution();
+		}
 	}
+	if (state & GDK_WINDOW_STATE_MAXIMIZED)
+	{
+		setMouseClipping(FALSE);
+		gtk_window_unmaximize(GTK_WINDOW(mWindow));
+	}
+}
 
-	return(result);
+BOOL LLWindowSDL::getVisible()
+{
+	return mWindow && gdk_window_is_visible(mWindow->window);
 }
 
-BOOL LLWindowSDL::maximize()
+BOOL LLWindowSDL::getMinimized()
 {
-	// TODO
-	return FALSE;
+	return mWindow && 0 != (gdk_window_get_state(mWindow->window) & GDK_WINDOW_STATE_ICONIFIED);
 }
 
-BOOL LLWindowSDL::getFullscreen()
+BOOL LLWindowSDL::getMaximized()
 {
-	return mFullscreen;
+	return mWindow && 0 != (gdk_window_get_state(mWindow->window) & GDK_WINDOW_STATE_MAXIMIZED);
 }
 
 BOOL LLWindowSDL::getPosition(LLCoordScreen *position)
 {
-    // *FIX: can anything be done with this?
-	position->mX = 0;
-	position->mY = 0;
-    return TRUE;
+    if (!mWindow)
+    {
+	    return FALSE;
+	}
+
+	position->mX = mWindow->allocation.x;
+	position->mY = mWindow->allocation.y;
+	return TRUE;
 }
 
 BOOL LLWindowSDL::getSize(LLCoordScreen *size)
 {
-    if (mWindow)
+    if (!mWindow)
     {
-        size->mX = mWindow->w;
-        size->mY = mWindow->h;
-		return (TRUE);
-    }
+	    return FALSE;
+	}
 
-	llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
-    return (FALSE);
+	size->mX = mWindow->allocation.width;
+	size->mY = mWindow->allocation.height;
+	return TRUE;
 }
 
 BOOL LLWindowSDL::getSize(LLCoordWindow *size)
 {
-    if (mWindow)
+	// I believe the following is the equivalent behaviour to
+	// LLWindowWin32::getSize(LLXCoordWindow*), but in GTK, mWindow
+	// and mGLWidget have exactly same size, so we could have just one
+	// method impl...
+
+    if (!mWindow)
     {
-        size->mX = mWindow->w;
-        size->mY = mWindow->h;
-		return (TRUE);
-    }
+	    return FALSE;
+	}
 
-	llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
-    return (FALSE);
+	size->mX = mGLWidget->allocation.width;
+	size->mY = mGLWidget->allocation.height;
+	return TRUE;
 }
 
 BOOL LLWindowSDL::setPosition(const LLCoordScreen position)
 {
 	if(mWindow)
 	{
-        // *FIX: (???)
-		//MacMoveWindow(mWindow, position.mX, position.mY, false);
+		gtk_window_move(GTK_WINDOW(mWindow), position.mX, position.mY);
 	}
 
 	return TRUE;
@@ -957,8 +1179,7 @@
 {
 	if(mWindow)
 	{
-        // *FIX: (???)
-		//SizeWindow(mWindow, size.mX, size.mY, true);
+		gtk_window_resize(GTK_WINDOW(mWindow), size.mX, size.mY);
 	}
 
 	return TRUE;
@@ -967,110 +1188,235 @@
 void LLWindowSDL::swapBuffers()
 {
 	if (mWindow)
-		SDL_GL_SwapBuffers();
+	{
+		GdkGLDrawable *gl_drawable = gtk_widget_get_gl_drawable(mGLWidget);
+
+		// Our GL drawable is always double buffered.  The following
+		// condition is just for a safe guard.
+		if (gdk_gl_drawable_is_double_buffered(gl_drawable))
+		{
+			gdk_gl_drawable_swap_buffers(gl_drawable);
+		}
+	}
 }
 
 F32 LLWindowSDL::getGamma()
 {
-	return 1/mGamma;
+	return mGamma;
 }
 
 BOOL LLWindowSDL::restoreGamma()
 {
-	//CGDisplayRestoreColorSyncSettings();
-    SDL_SetGamma(1.0f, 1.0f, 1.0f);
-	return true;
+	// We should save initial gamma ramp tables and restore them here.
+	// FIXME.
+	return setGamma(1);
 }
 
-BOOL LLWindowSDL::setGamma(const F32 gamma)
+// Calculate a 16 bit gamma corrected level for a given intensity value.
+static inline U16 ll_gamma(F32 value, F32 gamma)
 {
-	mGamma = gamma;
-	if (mGamma == 0) mGamma = 0.1f;
-	mGamma = 1/mGamma;
-	SDL_SetGamma(mGamma, mGamma, mGamma);
-	return true;
+	return llround(powf(value, gamma) * 65535);
 }
 
-BOOL LLWindowSDL::isCursorHidden()
+// Try to initialize the window to use DirectColor visual so that
+// gamma adjustment by direct color colormap method works.  We need to
+// do this after creating the GTK window and before mapping/realizing
+// it.
+void LLWindowSDL::initializeGamma()
 {
-	return mCursorHidden;
+	GdkVisual *visual = gdk_visual_get_best_with_type(GDK_VISUAL_DIRECT_COLOR);
+	if (visual &&
+		visual->type == GDK_VISUAL_DIRECT_COLOR &&
+		visual->bits_per_rgb >= 8)
+	{
+		// Yes, this visual is what we need.  Use it.
+		GdkColormap *colormap = gdk_colormap_new(visual, TRUE);
+
+		const F32 scale = 1.f / (colormap->size - 1);
+		for (int i = 0; i < colormap->size; i++)
+		{
+			// Although undocument, gdk_colormap_change() ignores
+			// GdkColor::pixel for DirectColor visuals, so we don't
+			// care about it.
+			GdkColor &color = colormap->colors[i];
+			const guint16 level = ll_gamma(i * scale, 1);
+			color.red = level;
+			color.green = level;
+			color.blue = level;
+		}
+		gdk_colormap_change(colormap, colormap->size);
+		gtk_widget_set_colormap(mWindow, colormap);
+	}
 }
 
+static const char GAMMA_DIRECTCOLOR[] = "DirectColor colormap";
+static const char GAMMA_XRANDR[] = "XRandR Extension";
+static const char GAMMA_XF86VM[] = "XF86VideoMode Extension";
 
-
-// Constrains the mouse to the window.
-void LLWindowSDL::setMouseClipping( BOOL b )
+static void report_gamma_adjustment_method(const char * const method)
 {
-	//llinfos << "LLWindowSDL::setMouseClipping " << b << llendl;
-	// Just stash the requested state.  We'll simulate this when the cursor is hidden by decoupling.
-	mIsMouseClipping = b;
-    //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
-	adjustCursorDecouple();
+	static const char * previous = NULL;
+	if (method != previous)
+	{
+		llinfos << "Using " << method << " for gamma adjustment" << llendl;
+		previous = method;
+	}
 }
 
-BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position)
+BOOL LLWindowSDL::setGamma(F32 gamma)
 {
-	BOOL result = TRUE;
-	LLCoordScreen screen_pos;
+	// Let the gamma value be in a reasonable range.
+	mGamma = (gamma == 0 ? 1.f : llclamp(gamma, 0.1f, 10.f));
 
-	if (!convertCoords(position, &screen_pos))
+	// First, we try to use colormap to adjust the intensity level.
+	// This method should always be possible with GTK/GDK, but it
+	// requires a DirectColor visual.  It should work best under some
+	// high-end graphics cards (such as Wildcat products of blessed
+	// memory) with multiple hardware LUTs, since under such
+	// configuration, the gamma value set here affects this window
+	// only.  It also works good with entry level 3D graphics cards
+	// with single LUT, because the gamma takes place only when this
+	// window is active.  Other methods, XRandR or XF86VM, affects
+	// entire screen and the effect is persistent.
+	GdkColormap * const colormap = gtk_widget_get_colormap(mWindow);
+	if (GDK_VISUAL_DIRECT_COLOR == gdk_colormap_get_visual(colormap)->type)
 	{
-		return FALSE;
+		const F32 scale = 1.f / (colormap->size - 1);
+		for (int i = 0; i < colormap->size; i++)
+		{
+			GdkColor &color = colormap->colors[i];
+			const guint16 level = ll_gamma(i * scale, mGamma);
+			color.red = level;
+			color.green = level;
+			color.blue = level;
+		}
+		gdk_colormap_change(colormap, colormap->size);
+		report_gamma_adjustment_method(GAMMA_DIRECTCOLOR);
+		return TRUE;
 	}
 
-	//llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
+#if LL_XRANDR
+	// Try gamma manipulation API available in XRandR 1.2.  Well, I
+	// have never seen X server (display driver) that support XRandR
+	// 1.2 based gamma correction.  All implementations apparently
+	// support it, but returns a zero for XRRGetCrtcGammaSize(), that
+	// is unusable.  I can't say I have tested the following code...
+	if (mXRRVersion >= 12)
+	{
+		Display *x11_display = GDK_DISPLAY();
+		GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mWindow));
+		Window x11_root_window = RootWindow(x11_display, GDK_SCREEN_XNUMBER(screen));
+		
+		// Build a list of CRTCs that covers (part of) this window and
+		// provides usable gamma ramp table.
+		std::vector<RRCrtc> crtcs;
+		XRRScreenResources *screen_resources = XRRGetScreenResources(x11_display, x11_root_window);
+		const S32 window_left = mWindow->allocation.x;
+		const S32 window_top = mWindow->allocation.y;
+		const S32 window_right = window_left + mWindow->allocation.width;
+		const S32 window_bottom = window_top + mWindow->allocation.height;
+		for (int i = 0; i < screen_resources->ncrtc; i++)
+		{
+			const RRCrtc crtc = screen_resources->crtcs[i];
+			XRRCrtcInfo * const info = XRRGetCrtcInfo(x11_display, screen_resources, crtc);
+			if (info && None != info->mode
+				&& llmax((S32) info->x, window_left) < llmin((S32) (info->x + info->width), window_right)
+				&& llmax((S32) info->y, window_top) < llmin((S32) (info->y + info->height), window_bottom)
+				&& XRRGetCrtcGammaSize(x11_display, crtc) >= 3)
+			{
+				crtcs.push_back(crtc);
+			}
+			XRRFreeCrtcInfo(info);
+		}
+		XRRFreeScreenResources(screen_resources);
 
-    SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
+		if (!crtcs.empty())
+		{
+			// Update the gamma tables as requested.
+			for (int i = 0; i < crtcs.size(); i++)
+			{
+				const int ramps = XRRGetCrtcGammaSize(x11_display, crtcs[i]);
+				XRRCrtcGamma * const new_gamma_table = XRRAllocGamma(ramps);
+				const F32 scale = 1.f / (ramps - 1);
+				for (int r = 0; r < ramps; r++)
+				{
+					const int level = ll_gamma(r * scale, mGamma);
+					new_gamma_table->red[r] = level;
+					new_gamma_table->green[r] = level;
+					new_gamma_table->blue[r] = level;
+				}
+				XRRSetCrtcGamma(x11_display, crtcs[i], new_gamma_table);
+				XRRFreeGamma(new_gamma_table);
+			}
+			report_gamma_adjustment_method(GAMMA_XRANDR);
+			return TRUE;
+		}
+	}
+#endif
 
-	// Under certain circumstances, this will trigger us to decouple the cursor.
-	adjustCursorDecouple(true);
+#if LL_XF86VM
+	// Try XF86 Video Mode 2.0 method as a last resort.
+	if (mXF86VMVersion >= 20)
+	{
+		Display *x11_display = GDK_DISPLAY();
+		XF86VidModeGamma vid_mode_gamma;
+		memset(&vid_mode_gamma, 0, sizeof vid_mode_gamma);
+		// XF86VM gamma is inverted...
+		F32 xf86vm_gamma = 1.f / mGamma;
+		vid_mode_gamma.red = xf86vm_gamma;
+		vid_mode_gamma.green = xf86vm_gamma;
+		vid_mode_gamma.blue = xf86vm_gamma;
+		GdkScreen * screen = gtk_window_get_screen(GTK_WINDOW(mWindow));
+		Status result = XF86VidModeSetGamma(x11_display, GDK_SCREEN_XNUMBER(screen), &vid_mode_gamma);
+		if (result)
+		{
+			report_gamma_adjustment_method(GAMMA_XF86VM);
+			return TRUE;
+		}
+	}
+#endif
 
-	return result;
+	// We couldn't change gamma.
+	return FALSE;
 }
 
-BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position)
+// Constrains the mouse to the window.
+void LLWindowSDL::setMouseClipping( BOOL b )
 {
-	//Point cursor_point;
-	LLCoordScreen screen_pos;
-
-	//GetMouse(&cursor_point);
-    int x, y;
-    SDL_GetMouseState(&x, &y);
-
-	screen_pos.mX = x;
-	screen_pos.mY = y;
-
-	return convertCoords(screen_pos, position);
+	// We need gdk_pointer_grab() to restrict the mouse pointer
+	// movement.  However, gdk_pointer_grab is already used by
+	// reallyCaptureInput(BOOL) to hack some window managers.
+	// It is not easy to make them co-exist.
+	// 
+	// We give up mouse clipping.
+	//
+	// In a future, we may eventually add this functionality by
+	// somehow mediating reallyCaptureInput(BOOL) and this method.
 }
 
-void LLWindowSDL::adjustCursorDecouple(bool warpingMouse)
+BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position_window)
 {
-	if(mIsMouseClipping && mCursorHidden)
+	LLCoordScreen position_screen;
+	if (!convertCoords(position_window, &position_screen))
 	{
-		if(warpingMouse)
-		{
-			// The cursor should be decoupled.  Make sure it is.
-			if(!mCursorDecoupled)
-			{
-				//			llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
-				//CGAssociateMouseAndMouseCursorPosition(false);
-				mCursorDecoupled = true;
-				mCursorIgnoreNextDelta = TRUE;
-			}
-		}
+		return FALSE;
 	}
-	else
-	{
-		// The cursor should not be decoupled.  Make sure it isn't.
-		if(mCursorDecoupled)
-		{
-			//			llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
-			//CGAssociateMouseAndMouseCursorPosition(true);
-			mCursorDecoupled = false;
-		}
-	}
+
+	GdkDisplay * display = gtk_widget_get_display(mWindow);
+	GdkScreen * screen = gtk_widget_get_screen(mWindow);
+	gdk_display_warp_pointer(display, screen, position_screen.mX, position_screen.mY);
+	return TRUE;
 }
 
+BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position_window)
+{
+    gint x, y;
+	gdk_display_get_pointer(gtk_widget_get_display(mWindow), NULL, &x, &y, NULL);
+
+	const LLCoordScreen position_screen(x, y);
+	return convertCoords(position_screen, position_window);
+}
+
 F32 LLWindowSDL::getNativeAspectRatio()
 {
 #if 0
@@ -1080,7 +1426,6 @@
 	S32 num_resolutions;
 	LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
 
-
 	return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
 	//rn: AC
 #endif
@@ -1102,18 +1447,30 @@
 	// The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
 	// switching, and stashes it in mOriginalAspectRatio.  Here, we just return it.
 
+	// -----------------------
+
+	// All the comments above this are inherited from the older SDL
+	// version.  What we are doing here is similar to the last
+	// commented behaviour; that is, grabbing the monitor pixel
+	// dimension before entering fullscreen, and assuming pixels are
+	// square.  If we really need physically correct aspect ratio of
+	// the monitor, we can query the physical dimension (i.e., width
+	// and height in millimeters) for each operating mode through
+	// XRandR that are taken from the display device itself through
+	// EDID.  I'm not sure it's worth doing so.
+
 	if (mOverrideAspectRatio > 0.f)
 	{
 		return mOverrideAspectRatio;
 	}
 
-	return mOriginalAspectRatio;
+	return (F32) mOriginalMonitorWidth / (F32) mOriginalMonitorHeight;
 }
 
 F32 LLWindowSDL::getPixelAspectRatio()
 {
 	F32 pixel_aspect = 1.f;
-	if (getFullscreen())
+	if (mFullscreen)
 	{
 		LLCoordScreen screen_size;
 		getSize(&screen_size);
@@ -1127,57 +1484,15 @@
 // some of this stuff is to support 'temporarily windowed' mode so that
 // dialogs are still usable in fullscreen.  HOWEVER! - it's not enabled/working
 // yet.
-static LLCoordScreen old_size;
-static BOOL old_fullscreen;
+
 void LLWindowSDL::beforeDialog()
 {
-	llinfos << "LLWindowSDL::beforeDialog()" << llendl;
-
-	if (SDLReallyCaptureInput(FALSE) // must ungrab input so popup works!
-	    && getSize(&old_size))
-	{
-		old_fullscreen = was_fullscreen;
-		
-		if (old_fullscreen)
-		{
-			// NOT YET WORKING
-			//switchContext(FALSE, old_size, TRUE);
-		}
-	}
-
-#if LL_X11
-	if (mSDL_Display)
-	{
-		// Everything that we/SDL asked for should happen before we
-		// potentially hand control over to GTK.
-		maybe_lock_display();
-		XSync(mSDL_Display, False);
-		maybe_unlock_display();
-	}
-#endif // LL_X11
-
-#if LL_GTK
-	// this is a good time to grab some GTK version information for
-	// diagnostics, if not already done.
-	ll_try_gtk_init();
-#endif // LL_GTK
-
-	maybe_lock_display();
+	// Nothing special to do.
 }
 
 void LLWindowSDL::afterDialog()
 {
-	llinfos << "LLWindowSDL::afterDialog()" << llendl;
-
-	maybe_unlock_display();
-
-	if (old_fullscreen && !was_fullscreen)
-	{
-		// *FIX: NOT YET WORKING (see below)
-		//switchContext(TRUE, old_size, TRUE);
-	}
-	// *FIX: we need to restore the GL context using
-	// LLViewerWindow::restoreGL() - but how??
+	// Nothing special to do.
 }
 
 
@@ -1186,664 +1501,111 @@
 	return ::stat( file_name, stat_info );
 }
 
-#if LL_X11
-// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
-void LLWindowSDL::x11_set_urgent(BOOL urgent)
-{
-	if (mSDL_Display && !mFullscreen)
-	{
-		XWMHints *wm_hints;
-		
-		llinfos << "X11 hint for urgency, " << urgent << llendl;
-
-		maybe_lock_display();
-		wm_hints = XGetWMHints(mSDL_Display, mSDL_XWindowID);
-		if (!wm_hints)
-			wm_hints = XAllocWMHints();
-
-		if (urgent)
-			wm_hints->flags |= XUrgencyHint;
-		else
-			wm_hints->flags &= ~XUrgencyHint;
-
-		XSetWMHints(mSDL_Display, mSDL_XWindowID, wm_hints);
-		XFree(wm_hints);
-		XSync(mSDL_Display, False);
-		maybe_unlock_display();
-	}
-}
-#endif // LL_X11
-
 void LLWindowSDL::flashIcon(F32 seconds)
 {
-#if !LL_X11
-	llinfos << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
-#else	
-	llinfos << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
-	
 	F32 remaining_time = mFlashTimer.getRemainingTimeF32();
 	if (remaining_time < seconds)
 		remaining_time = seconds;
 	mFlashTimer.reset();
 	mFlashTimer.setTimerExpirySec(remaining_time);
 
-	x11_set_urgent(TRUE);
+	gtk_window_set_urgency_hint(GTK_WINDOW(mWindow), TRUE);
 	mFlashing = TRUE;
-#endif // LL_X11
 }
 
-#if LL_X11
-/* Lots of low-level X11 stuff to handle X11 copy-and-paste */
 
-/* Our X11 clipboard support is a bit bizarre in various
-   organically-grown ways.  Ideally it should be fixed to do
-   real string-type negotiation (this would make pasting to
-   xterm faster and pasting to UTF-8 emacs work properly), but
-   right now it has the rare and desirable trait of being
-   generally stable and working. */
-
-typedef Atom x11clipboard_type;
-
-/* PRIMARY and CLIPBOARD are the two main kinds of
-   X11 clipboard.  A third are the CUT_BUFFERs which an
-   obsolete holdover from X10 days and use a quite orthogonal
-   mechanism.  CLIPBOARD is the type whose design most
-   closely matches SL's own win32-alike explicit copy-and-paste
-   paradigm.
-
-   Pragmatically we support all three to varying degrees.  When
-   we paste into SL, it is strictly from CLIPBOARD.  When we copy,
-   we support (to as full an extent as the clipboard content type
-   allows) CLIPBOARD, PRIMARY, and CUT_BUFFER0.
- */
-static x11clipboard_type get_x11_readwrite_clipboard_type(void)
-{
-	return XInternAtom(get_SDL_Display(), "CLIPBOARD", False);
-}
-
-static x11clipboard_type get_x11_write_clipboard_type(void)
-{
-	return XA_PRIMARY;
-}
-
-/* This is where our own private cutbuffer goes - we don't use
-   a regular cutbuffer (XA_CUT_BUFFER0 etc) for intermediate
-   storage because their use isn't really defined for holding UTF8. */
-static x11clipboard_type get_x11_cutbuffer_clipboard_type(void)
-{
-	return XInternAtom(get_SDL_Display(), "SECONDLIFE_CUTBUFFER", False);
-}
-
-/* Some X11 atom-generators */
-static Atom get_x11_targets_atom(void)
-{
-	return XInternAtom(get_SDL_Display(), "TARGETS", False);
-}
-
-static Atom get_x11_text_atom(void)
-{
-	return XInternAtom(get_SDL_Display(), "TEXT", False);
-}
-
-/* These defines, and convert_data/convert_x11clipboard,
-   mostly exist to support non-text or unusually-encoded
-   clipboard data, which we don't really have a need for at
-   the moment. */
-#define SDLCLIPTYPE(A, B, C, D) (int)(((A)<<24)|((B)<<16)|((C)<<8)|((D)<<0))
-#define FORMAT_PREFIX	"SECONDLIFE_x11clipboard_0x"
-
-static
-x11clipboard_type convert_format(int type)
-{
-	if (!gWindowImplementation)
-	{
-		llwarns << "!gWindowImplementation in convert_format()"
-			<< llendl;
-		return XA_STRING;
-	}
-
-	switch (type)
-	{
-	case SDLCLIPTYPE('T', 'E', 'X', 'T'):
-		// old-style X11 clipboard, strictly only ISO 8859-1 encoding
-		return XA_STRING;
-	case SDLCLIPTYPE('U', 'T', 'F', '8'):
-		// newer de-facto UTF8 clipboard atom
-		return XInternAtom(gWindowImplementation->mSDL_Display,
-				   "UTF8_STRING", False);
-	default:
-	{
-		/* completely arbitrary clipboard types... we don't actually use
-		these right now, and support is skeletal. */
-		char format[sizeof(FORMAT_PREFIX)+8+1];	/* Flawfinder: ignore */
-
-		snprintf(format, sizeof(format), "%s%08lx", FORMAT_PREFIX, (unsigned long)type);
-		return XInternAtom(gWindowImplementation->mSDL_Display,
-				   format, False);
-	}
-    }
-}
-
-/* convert platform string to x11 clipboard format.  for our
-   purposes this is pretty trivial right now. */
-static int
-convert_data(int type, char *dst, const char *src, int srclen)
-{
-	int dstlen;
-
-	dstlen = 0;
-	switch (type)
-	{
-	case SDLCLIPTYPE('T', 'E', 'X', 'T'):
-	case SDLCLIPTYPE('U', 'T', 'F', '8'):
-		if (src == NULL)
-		{
-			break;
-		}
-		if ( srclen == 0 )
-			srclen = strlen(src);	/* Flawfinder: ignore */
-		
-		dstlen = srclen + 1;
-		
-		if ( dst ) // assume caller made it big enough by asking us
-		{
-			memcpy(dst, src, srclen);	/* Flawfinder: ignore */
-			dst[srclen] = '\0';
-		}
-		break;
-		
-	default:
-		llwarns << "convert_data: Unknown medium type" << llendl;
-		break;
-	}
-	return(dstlen);
-}
-
-/* Convert x11clipboard data to platform string.  This too is
-   pretty trivial for our needs right now, and just about identical
-   to above. */
-static int
-convert_x11clipboard(int type, char *dst, const char *src, int srclen)
-{
-	int dstlen;
-
-	dstlen = 0;
-	switch (type)
-	{
-	case SDLCLIPTYPE('U', 'T', 'F', '8'):
-	case SDLCLIPTYPE('T', 'E', 'X', 'T'):
-		if (src == NULL)
-		{
-			break;
-		}
-		if ( srclen == 0 )
-			srclen = strlen(src);	/* Flawfinder: ignore */
-		
-		dstlen = srclen + 1;
-		
-		if ( dst ) // assume caller made it big enough by asking us
-		{
-			memcpy(dst, src, srclen);	/* Flawfinder: ignore */
-			dst[srclen] = '\0';
-		}
-		break;
-		
-	default:
-		llwarns << "convert_x11clipboard: Unknown medium type" << llendl;
-		break;
-	}
-	return dstlen;
-}
-
-int
-LLWindowSDL::is_empty_x11clipboard(void)
-{
-	int retval;
-
-	maybe_lock_display();
-	retval = ( XGetSelectionOwner(mSDL_Display, get_x11_readwrite_clipboard_type()) == None );
-	maybe_unlock_display();
-
-	return(retval);
-}
-
-void
-LLWindowSDL::put_x11clipboard(int type, int srclen, const char *src)
-{
-	x11clipboard_type format;
-	int dstlen;
-	char *dst;
-
-	format = convert_format(type);
-	dstlen = convert_data(type, NULL, src, srclen);
-
-	dst = (char *)malloc(dstlen);
-	if ( dst != NULL )
-	{
-		maybe_lock_display();
-		Window root = DefaultRootWindow(mSDL_Display);
-		convert_data(type, dst, src, srclen);
-		// Cutbuffers are only allowed to have STRING atom types,
-		// but Emacs puts UTF8 inside them anyway.  We cautiously
-		// don't.
-		if (type == SDLCLIPTYPE('T','E','X','T'))
-		{
-			// dstlen-1 so we don't include the trailing \0
-			llinfos << "X11: Populating cutbuffer." <<llendl;
-			XChangeProperty(mSDL_Display, root,
-					XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace,
-					(unsigned char*)dst, dstlen-1);
-		} else {
-			// Should we clear the cutbuffer if we can't put the selection in
-			// it because it's a UTF8 selection?  Eh, no great reason I think.
-			//XDeleteProperty(SDL_Display, root, XA_CUT_BUFFER0);
-		}
-		// Private cutbuffer of an appropriate type.
-		XChangeProperty(mSDL_Display, root,
-				get_x11_cutbuffer_clipboard_type(), format, 8, PropModeReplace,
-				(unsigned char*)dst, dstlen-1);
-		free(dst);
-		
-		/* Claim ownership of both PRIMARY and CLIPBOARD */
-		XSetSelectionOwner(mSDL_Display, get_x11_readwrite_clipboard_type(),
-				   mSDL_XWindowID, CurrentTime);
-		XSetSelectionOwner(mSDL_Display, get_x11_write_clipboard_type(),
-				   mSDL_XWindowID, CurrentTime);
-		
-		maybe_unlock_display();
-	}
-}
-
-void
-LLWindowSDL::get_x11clipboard(int type, int *dstlen, char **dst)
-{
-	x11clipboard_type format;
-	
-	*dstlen = 0;
-	format = convert_format(type);
-
-	Window owner;
-	Atom selection;
-	Atom seln_type;
-	int seln_format;
-	unsigned long nbytes;
-	unsigned long overflow;
-	char *src;
-	
-	maybe_lock_display();
-	owner = XGetSelectionOwner(mSDL_Display, get_x11_readwrite_clipboard_type());
-	maybe_unlock_display();
-	if (owner == None)
-	{
-		// Fall right back to ancient X10 cut-buffers
-		owner = DefaultRootWindow(mSDL_Display);
-		selection = XA_CUT_BUFFER0;
-	} else if (owner == mSDL_XWindowID)
-	{
-		// Use our own uncooked opaque string property
-		owner = DefaultRootWindow(mSDL_Display);
-		selection = get_x11_cutbuffer_clipboard_type();
-	}
-	else
-	{
-		// Use full-on X11-style clipboard negotiation with the owning app
-		int selection_response = 0;
-		SDL_Event event;
-		
-		owner = mSDL_XWindowID;
-		maybe_lock_display();
-		selection = XInternAtom(mSDL_Display, "SDL_SELECTION", False);
-		XConvertSelection(mSDL_Display, get_x11_readwrite_clipboard_type(), format,
-				  selection, owner, CurrentTime);
-		maybe_unlock_display();
-		llinfos << "X11: Waiting for clipboard to arrive." <<llendl;
-		while ( ! selection_response )
-		{
-			// Only look for SYSWMEVENTs, or we may lose keypresses
-			// etc.
-			SDL_PumpEvents();
-			if (1 == SDL_PeepEvents(&event, 1, SDL_GETEVENT,
-						SDL_SYSWMEVENTMASK) )
-			{
-				if ( event.type == SDL_SYSWMEVENT )
-				{
-					XEvent xevent =
-						event.syswm.msg->event.xevent;
-				
-					if ( (xevent.type == SelectionNotify)&&
-					     (xevent.xselection.requestor == owner) )
-						selection_response = 1;
-				}
-			} else {
-				llinfos << "X11: Waiting for SYSWM event..." <<  llendl;
-			}
-		}
-		llinfos << "X11: Clipboard arrived." <<llendl;
-	}
-
-	maybe_lock_display();
-	if ( XGetWindowProperty(mSDL_Display, owner, selection, 0, INT_MAX/4,
-				False, format, &seln_type, &seln_format,
-				&nbytes, &overflow, (unsigned char **)&src) == Success )
-	{
-		if ( seln_type == format )
-		{
-			*dstlen = convert_x11clipboard(type, NULL, src, nbytes);
-			*dst = (char *)realloc(*dst, *dstlen);
-			if ( *dst == NULL )
-				*dstlen = 0;
-			else
-				convert_x11clipboard(type, *dst, src, nbytes);
-		}
-		XFree(src);
-	}
-	maybe_unlock_display();
-}
-
-int clipboard_filter_callback(const SDL_Event *event)
-{
-	/* Post all non-window manager specific events */
-	if ( event->type != SDL_SYSWMEVENT )
-	{
-		return(1);
-	}
-
-	/* Handle window-manager specific clipboard events */
-	switch (event->syswm.msg->event.xevent.type) {
-	/* Copy the selection from SECONDLIFE_CUTBUFFER to the requested property */
-	case SelectionRequest: {
-		XSelectionRequestEvent *req;
-		XEvent sevent;
-		int seln_format;
-		unsigned long nbytes;
-		unsigned long overflow;
-		unsigned char *seln_data;
-
-		req = &event->syswm.msg->event.xevent.xselectionrequest;
-		sevent.xselection.type = SelectionNotify;
-		sevent.xselection.display = req->display;
-		sevent.xselection.selection = req->selection;
-		sevent.xselection.target = None;
-		sevent.xselection.property = None;
-		sevent.xselection.requestor = req->requestor;
-		sevent.xselection.time = req->time;
-		if ( XGetWindowProperty(get_SDL_Display(), DefaultRootWindow(get_SDL_Display()),
-					get_x11_cutbuffer_clipboard_type(), 0, INT_MAX/4, False, req->target,
-					&sevent.xselection.target, &seln_format,
-					&nbytes, &overflow, &seln_data) == Success )
-		{
-			if ( sevent.xselection.target == req->target)
-			{
-				if ( sevent.xselection.target == XA_STRING ||
-				     sevent.xselection.target ==
-				     convert_format(SDLCLIPTYPE('U','T','F','8')) )
-				{
-					if ( seln_data[nbytes-1] == '\0' )
-						--nbytes;
-				}
-				XChangeProperty(get_SDL_Display(), req->requestor, req->property,
-						req->target, seln_format, PropModeReplace,
-						seln_data, nbytes);
-				sevent.xselection.property = req->property;
-			} else if (get_x11_targets_atom() == req->target) {
-				/* only advertise what we currently support */
-				const int num_supported = 3;
-				Atom supported[num_supported] = {
-					XA_STRING, // will be over-written below
-					get_x11_text_atom(),
-					get_x11_targets_atom()
-				};
-				supported[0] = sevent.xselection.target;
-				XChangeProperty(get_SDL_Display(), req->requestor,
-						req->property, XA_ATOM, 32, PropModeReplace,
-						(unsigned char*)supported,
-						num_supported);
-				sevent.xselection.property = req->property;
-				llinfos << "Clipboard: An app asked us what selections format we offer." << llendl;
-			} else {
-				llinfos << "Clipboard: An app requested an unsupported selection format " << req->target << ", we have " << sevent.xselection.target << llendl;
-			    sevent.xselection.target = None;
-			}
-			XFree(seln_data);
-		}
-		int sendret =
-			XSendEvent(get_SDL_Display(),req->requestor,False,0,&sevent);
-		if ((sendret==BadValue) || (sendret==BadWindow))
-			llwarns << "Clipboard SendEvent failed" << llendl;
-		XSync(get_SDL_Display(), False);
-	}
-		break;
-	}
-	
-	/* Post the event for X11 clipboard reading above */
-	return(1);
-}
-
-int
-LLWindowSDL::init_x11clipboard(void)
-{
-	SDL_SysWMinfo info;
-	int retval;
-
-	/* Grab the window manager specific information */
-	retval = -1;
-	SDL_SetError("SDL is not running on known window manager");
-
-	SDL_VERSION(&info.version);
-	if ( SDL_GetWMInfo(&info) )
-	{
-		/* Save the information for later use */
-		if ( info.subsystem == SDL_SYSWM_X11 )
-		{
-			mSDL_Display = info.info.x11.display;
-			mSDL_XWindowID = info.info.x11.wmwindow;
-			Lock_Display = info.info.x11.lock_func;
-			Unlock_Display = info.info.x11.unlock_func;
-			
-			/* Enable the special window hook events */
-			SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
-			SDL_SetEventFilter(clipboard_filter_callback);
-			
-			retval = 0;
-		}
-		else
-		{
-			SDL_SetError("SDL is not running on X11");
-		}
-	}
-	return(retval);
-}
-
-void
-LLWindowSDL::quit_x11clipboard(void)
-{
-	mSDL_Display = NULL;
-	mSDL_XWindowID = None;
-	Lock_Display = NULL;
-	Unlock_Display = NULL;
-
-	SDL_SetEventFilter(NULL); // Stop custom event filtering
-}
-
 /************************************************/
 
 BOOL LLWindowSDL::isClipboardTextAvailable()
 {
-	return !is_empty_x11clipboard();
+	GtkClipboard * const clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+	return gtk_clipboard_wait_is_text_available(clipboard);
 }
 
-BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
+BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &text)
 {
-	int cliplen; // seems 1 or 2 bytes longer than expected
-	char *cliptext = NULL;
-	get_x11clipboard(SDLCLIPTYPE('U','T','F','8'), &cliplen, &cliptext);
-	if (cliptext)
+	GtkClipboard * const clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+	gchar * const data = gtk_clipboard_wait_for_text(clipboard);
+	if (data)
 	{
-		llinfos << "X11: Got UTF8 clipboard text." << llendl;
-		// at some future time we can use cliplen instead of relying on \0,
-		// if we ever grok non-ascii, non-utf8 encodings on the clipboard.
-		std::string clip_str(cliptext);
-		// we can't necessarily trust the incoming text to be valid UTF-8,
-		// but utf8str_to_wstring() seems to do an appropriate level of
-		// validation for avoiding over-reads.
-		dst = utf8str_to_wstring(clip_str);
-		/*llinfos << "X11 pasteTextFromClipboard: cliplen=" << cliplen <<
-			" strlen(cliptext)=" << strlen(cliptext) <<
-			" clip_str.length()=" << clip_str.length() <<
-			" dst.length()=" << dst.length() <<
-			llendl;*/
-		free(cliptext);
-		return TRUE; // success
+		text = LLWString(utf8str_to_wstring(data));		
+		g_free(data);
+		return TRUE;
 	}
-	get_x11clipboard(SDLCLIPTYPE('T','E','X','T'), &cliplen, &cliptext);
-	if (cliptext)
-	{
-		llinfos << "X11: Got ISO 8859-1 clipboard text." << llendl;
-		std::string clip_str(cliptext);
-		std::string utf8_str = rawstr_to_utf8(clip_str);
-		dst = utf8str_to_wstring(utf8_str);
-		free(cliptext);
-	}
-	return FALSE; // failure
+	return FALSE;
 }
 
-BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
+BOOL LLWindowSDL::copyTextToClipboard(const LLWString &text)
 {
-	std::string utf8text = wstring_to_utf8str(s);
-	const char* cstr = utf8text.c_str();
-	if (cstr == NULL)
-	{
-		return FALSE;
-	}
-	int cstrlen = strlen(cstr);	/* Flawfinder: ignore */
-	int i;
-	for (i=0; i<cstrlen; ++i)
-	{
-		if (0x80 & (unsigned char)cstr[i])
-		{
-			// Found an 8-bit character; use new-style UTF8 clipboard
-			llinfos << "X11: UTF8 copyTextToClipboard" << llendl;
-			put_x11clipboard(SDLCLIPTYPE('U','T','F','8'), cstrlen, cstr);
-			return TRUE;
-		}
-	}
-	// Didn't find any 8-bit characters; use old-style ISO 8859-1 clipboard
-	llinfos << "X11: ISO 8859-1 copyTextToClipboard" << llendl;
-	put_x11clipboard(SDLCLIPTYPE('T','E','X','T'), cstrlen, cstr);
+	const std::string utf8 = wstring_to_utf8str(text);
+	GtkClipboard * const clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+	gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length());
 	return TRUE;
 }
-#else
 
-BOOL LLWindowSDL::isClipboardTextAvailable()
-{
-	return FALSE; // unsupported
-}
+/***********************************************/
 
-BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
-{
-	return FALSE; // unsupported
-}
-
-BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
-{
-	return FALSE;  // unsupported
-}
-#endif // LL_X11
-
 BOOL LLWindowSDL::sendEmail(const char* address, const char* subject, const char* body_text,
 									   const char* attachment, const char* attachment_displayed_name )
 {
-	// MBW -- XXX -- Um... yeah.  I'll get to this later.
-
 	return FALSE;
 }
 
 
-LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
+BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
 {
-	if (!mSupportedResolutions)
+	if (!to)
 	{
-		mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
-		mNumSupportedResolutions = 0;
-
-        SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
-        if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
-        {
-            int count = 0;
-            while (*modes)  // they're sorted biggest to smallest, so find end...
-            {
-                modes++;
-                count++;
-            }
-
-            while (count--)
-            {
-                modes--;
-                SDL_Rect *r = *modes;
-                int w = r->w;
-                int h = r->h;
-                if ((w >= 800) && (h >= 600))
-                {
-                    // make sure we don't add the same resolution multiple times!
-                    if ( (mNumSupportedResolutions == 0) ||
-                         ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
-                          (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
-                    {
-                        mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
-                        mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
-                        mNumSupportedResolutions++;
-                    }
-                }
-            }
-        }
+		return FALSE;
 	}
-
-	num_resolutions = mNumSupportedResolutions;
-	return mSupportedResolutions;
-}
-
-BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
-{
-    if (!to)
-        return FALSE;
-
+	
 	to->mX = from.mX;
-	to->mY = mWindow->h - from.mY - 1;
+	to->mY = mGLWidget->allocation.height - from.mY - 1;
 
 	return TRUE;
 }
 
 BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
 {
-    if (!to)
-        return FALSE;
-
+	if (!to)
+	{
+		return FALSE;
+	}
+	
 	to->mX = from.mX;
-	to->mY = mWindow->h - from.mY - 1;
+	to->mY = mGLWidget->allocation.height - from.mY - 1;
 
 	return TRUE;
 }
 
 BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
 {
-    if (!to)
+	if (!to)
+	{
 		return FALSE;
+	}
 
-	// In the fullscreen case, window and screen coordinates are the same.
-	to->mX = from.mX;
-	to->mY = from.mY;
-    return (TRUE);
+	gint x, y;
+	gdk_window_get_position(mWindow->window, &x, &y);
+	to->mX = from.mX - x;
+	to->mY = from.mY - y;
+
+	return TRUE;
 }
 
 BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
 {
-    if (!to)
+	if (!to)
+	{
 		return FALSE;
+	}
 
-	// In the fullscreen case, window and screen coordinates are the same.
-	to->mX = from.mX;
-	to->mY = from.mY;
-    return (TRUE);
+	gint x, y;
+	gdk_window_get_position(mWindow->window, &x, &y);
+	to->mX = from.mX + x;
+	to->mY = from.mY + y;
+
+	return TRUE;
 }
 
 BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
@@ -1860,9 +1622,6 @@
 	return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
 }
 
-
-
-
 void LLWindowSDL::setupFailure(const char* text, const char* caption, U32 type)
 {
 	destroyContext();
@@ -1870,90 +1629,52 @@
 	OSMessageBox(text, caption, type);
 }
 
-BOOL LLWindowSDL::SDLReallyCaptureInput(BOOL capture)
+void LLWindowSDL::reallyCaptureInput(BOOL capture)
 {
+	// Converted (and slightly simplified) from SDL versions' Xlib API
+	// based operation.
+
 	// note: this used to be safe to call nestedly, but in the
 	// end that's not really a wise usage pattern, so don't.
 
 	if (capture)
-		mReallyCapturedCount = 1;
-	else
-		mReallyCapturedCount = 0;
-	
-	SDL_GrabMode wantmode, newmode;
-	if (mReallyCapturedCount <= 0) // uncapture
 	{
-		wantmode = SDL_GRAB_OFF;
-	} else // capture
-	{
-		wantmode = SDL_GRAB_ON;
+		gdk_pointer_grab(mWindow->window, TRUE, (GdkEventMask)0, NULL, NULL, GDK_CURRENT_TIME);
 	}
-	
-	if (mReallyCapturedCount < 0) // yuck, imbalance.
+	else
 	{
-		mReallyCapturedCount = 0;
-		llwarns << "ReallyCapture count was < 0" << llendl;
+		gdk_pointer_ungrab(GDK_CURRENT_TIME);
 	}
+				
+	// SDL version calls XSync() only after ungrabbing,
+	// with the following comment:
+	// 
+	// // Make sure the ungrab happens RIGHT NOW.
+	// 
+	// I don't think it is important to ungrab quickly, since it
+	// occurs when a user releases a _grabby_ key to do something
+	// other than the operation he/she did with holding the key, and
+	// it is unusual to click very soon after releasing the key.  On
+	// the other hand, it may be important to grab as soon as possible
+	// after a user presses a grabby key, since he/she may press the
+	// key and mouse button almost simultaneously.  Moreover, I see no
+	// point waiting the completion of the grab/ungrab request here,
+	// since anyway the user may perform the _next_ action, regardless
+	// whether we are waiting or keep going.
+	// 
+	// Based on the above consideration, I decided to put a flush
+	// operation here for execution after both grabbing and
+	// ungrabbing.  Opinions?  -- Alissa
 
-	if (!mFullscreen) /* only bother if we're windowed anyway */
-	{
-#if LL_X11
-		if (mSDL_Display)
-		{
-			/* we dirtily mix raw X11 with SDL so that our pointer
-			   isn't (as often) constrained to the limits of the
-			   window while grabbed, which feels nicer and
-			   hopefully eliminates some reported 'sticky pointer'
-			   problems.  We use raw X11 instead of
-			   SDL_WM_GrabInput() because the latter constrains
-			   the pointer to the window and also steals all
-			   *keyboard* input from the window manager, which was
-			   frustrating users. */
-			int result;
-			if (wantmode == SDL_GRAB_ON)
-			{
-				//llinfos << "X11 POINTER GRABBY" << llendl;
-				//newmode = SDL_WM_GrabInput(wantmode);
-				maybe_lock_display();
-				result = XGrabPointer(mSDL_Display, mSDL_XWindowID,
-						      True, 0, GrabModeAsync,
-						      GrabModeAsync,
-						      None, None, CurrentTime);
-				maybe_unlock_display();
-				if (GrabSuccess == result)
-					newmode = SDL_GRAB_ON;
-				else
-					newmode = SDL_GRAB_OFF;
-			} else if (wantmode == SDL_GRAB_OFF)
-			{
-				//llinfos << "X11 POINTER UNGRABBY" << llendl;
-				newmode = SDL_GRAB_OFF;
-				//newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
-				
-				maybe_lock_display();
-				XUngrabPointer(mSDL_Display, CurrentTime);
-				// Make sure the ungrab happens RIGHT NOW.
-				XSync(mSDL_Display, False);
-				maybe_unlock_display();
-			} else
-			{
-				newmode = SDL_GRAB_QUERY; // neutral
-			}
-		} else // not actually running on X11, for some reason
-			newmode = wantmode;
-#endif // LL_X11
-	} else {
-		// pretend we got what we wanted, when really we don't care.
-		newmode = wantmode;
-	}
-	
-	// return boolean success for whether we ended up in the desired state
-	return (capture && SDL_GRAB_ON==newmode) ||
-		(!capture && SDL_GRAB_OFF==newmode);
+	gdk_display_flush(gtk_widget_get_display(mWindow));
 }
 
-U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain)
+U32 LLWindowSDL::checkGrabbyKeys(guint keysym, BOOL gain)
 {
+	// The following comments are from SDL version, but GDK shares
+	// same grabbing model and window manager environments with X11,
+	// so they apply.
+
 	/* part of the fix for SL-13243: Some popular window managers like
 	   to totally eat alt-drag for the purposes of moving windows.  We
 	   spoil their day by acquiring the exclusive X11 mouse lock for as
@@ -1967,492 +1688,649 @@
 	   instead of simply key-counting, because SDL sometimes reports
 	   misbalanced keyup/keydown event pairs to us for whatever reason. */
 
-	U32 mask = 0;
-	switch (keysym)
+  	U32 mask = 0;
+  	switch (keysym)
 	{
-	case SDLK_LALT:
-		mask = 1U << 0; break;
-	case SDLK_LCTRL:
-		mask = 1U << 1; break;
-	case SDLK_RCTRL:
-		mask = 1U << 2; break;
+	case GDK_Alt_L:
+		mask = 1U << 0;
+		break;
+	case GDK_Control_L:
+		mask = 1U << 1;
+		break;
+	case GDK_Control_R:
+		mask = 1U << 2;
+		break;
 	default:
 		break;
 	}
-
-	if (gain)
+  
+  	if (gain)
+	{
 		mGrabbyKeyFlags |= mask;
-	else
+	}
+  	else
+	{
 		mGrabbyKeyFlags &= ~mask;
-
-	//llinfos << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << llendl;
-
-	/* 0 means we don't need to mousegrab, otherwise grab. */
-	return mGrabbyKeyFlags;
+	}
+  
+  	/* 0 means we don't need to mousegrab, otherwise grab. */
+  	return mGrabbyKeyFlags;
 }
-
+  
 void LLWindowSDL::gatherInput()
 {
-    const Uint32 CLICK_THRESHOLD = 300;  // milliseconds
-    static int leftClick = 0;
-    static int rightClick = 0;
-    static Uint32 lastLeftDown = 0;
-    static Uint32 lastRightDown = 0;
-    SDL_Event event;
-
-#if LL_GTK && LL_LLMOZLIB_ENABLED
-    // Pump GTK events so embedded Gecko doesn't starve.
-    if (ll_try_gtk_init())
-    {
-	    // Yuck, Mozilla's GTK callbacks play with the locale - push/pop
-	    // the locale to protect it, as exotic/non-C locales
-	    // causes our code lots of general critical weirdness
-	    // and crashness. (SL-35450)
-	    std::string saved_locale = setlocale(LC_ALL, NULL);
-
-	    // Do a limited number of pumps so SL doesn't starve!
-	    // *TODO: this should ideally be time-limited, not count-limited.
-	    gtk_main_iteration_do(0); // Always do one non-blocking pump
-	    for (int iter=0; iter<10; ++iter)
-		    if (gtk_events_pending())
-			    gtk_main_iteration();
-
-	    setlocale(LC_ALL, saved_locale.c_str() );
-    }
-#endif // LL_GTK && LL_LLMOZLIB_ENABLED
-
-    // Handle all outstanding SDL events
-    while (SDL_PollEvent(&event))
-    {
-        switch (event.type)
-        {
-            case SDL_MOUSEMOTION:
-            {
-                LLCoordWindow winCoord(event.button.x, event.button.y);
-                LLCoordGL openGlCoord;
-                convertCoords(winCoord, &openGlCoord);
-				MASK mask = gKeyboard->currentMask(TRUE);
-				mCallbacks->handleMouseMove(this, openGlCoord, mask);
-                break;
-            }
-
-            case SDL_KEYDOWN:
-                gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod);
-		// part of the fix for SL-13243
-		if (SDLCheckGrabbyKeys(event.key.keysym.sym, TRUE) != 0)
-			SDLReallyCaptureInput(TRUE);
-
-                if (event.key.keysym.unicode)
-				{
-					handleUnicodeUTF16(event.key.keysym.unicode,
-									   gKeyboard->currentMask(FALSE));
-				}
-                break;
-
-            case SDL_KEYUP:
-		if (SDLCheckGrabbyKeys(event.key.keysym.sym, FALSE) == 0)
-			SDLReallyCaptureInput(FALSE); // part of the fix for SL-13243
-
-		gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod);
-                break;
-
-            case SDL_MOUSEBUTTONDOWN:
-            {
-                bool isDoubleClick = false;
-                LLCoordWindow winCoord(event.button.x, event.button.y);
-                LLCoordGL openGlCoord;
-                convertCoords(winCoord, &openGlCoord);
-		MASK mask = gKeyboard->currentMask(TRUE);
-
-                if (event.button.button == SDL_BUTTON_LEFT)   // SDL doesn't manage double clicking...
-                {
-                    Uint32 now = SDL_GetTicks();
-                    if ((now - lastLeftDown) > CLICK_THRESHOLD)
-                        leftClick = 1;
-                    else
-                    {
-                        if (++leftClick >= 2)
-                        {
-                            leftClick = 0;
-			    isDoubleClick = true;
-                        }
-                    }
-                    lastLeftDown = now;
-                }
-                else if (event.button.button == SDL_BUTTON_RIGHT)
-                {
-                    Uint32 now = SDL_GetTicks();
-                    if ((now - lastRightDown) > CLICK_THRESHOLD)
-                        rightClick = 1;
-                    else
-                    {
-                        if (++rightClick >= 2)
-                        {
-                            rightClick = 0;
-    					    isDoubleClick = true;
-                        }
-                    }
-                    lastRightDown = now;
-                }
-
-                if (event.button.button == SDL_BUTTON_LEFT)  // left
-                {
-                    if (isDoubleClick)
-				        mCallbacks->handleDoubleClick(this, openGlCoord, mask);
-                    else
-    				    mCallbacks->handleMouseDown(this, openGlCoord, mask);
-                }
-
-                else if (event.button.button == SDL_BUTTON_RIGHT)  // right ... yes, it's 3, not 2, in SDL...
-                {
-                    // right double click isn't handled right now in Second Life ... if (isDoubleClick)
-				    mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
-                }
-
-                else if (event.button.button == SDL_BUTTON_MIDDLE)  // middle
-				{
-				    mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
-				}
-                else if (event.button.button == 4)  // mousewheel up...thanks to X11 for making SDL consider these "buttons".
-					mCallbacks->handleScrollWheel(this, -1);
-                else if (event.button.button == 5)  // mousewheel down...thanks to X11 for making SDL consider these "buttons".
-					mCallbacks->handleScrollWheel(this, 1);
-
-                break;
-            }
-
-            case SDL_MOUSEBUTTONUP:
-            {
-                LLCoordWindow winCoord(event.button.x, event.button.y);
-                LLCoordGL openGlCoord;
-                convertCoords(winCoord, &openGlCoord);
-		MASK mask = gKeyboard->currentMask(TRUE);
-
-                if (event.button.button == SDL_BUTTON_LEFT)  // left
-				    mCallbacks->handleMouseUp(this, openGlCoord, mask);
-                else if (event.button.button == SDL_BUTTON_RIGHT)  // right ... yes, it's 3, not 2, in SDL...
-				    mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
-                else if (event.button.button == SDL_BUTTON_MIDDLE)  // middle
+ 	//  XXX  Are we still current in GL?  XXX
+ 	GdkGLContext * const gl_context = gtk_widget_get_gl_context(mGLWidget);
+ 	if (gdk_gl_context_get_current() != gl_context)
+	{
+		llwarns << "*** OOPS! someone took GL rendering context!  Recovering..." << llendl;			
+		GdkGLDrawable * const gl_drawable = gtk_widget_get_gl_drawable(mGLWidget);
+		gdk_gl_drawable_gl_begin(gl_drawable, gl_context);
+	}
+  
+ 	// The following comment is still valid, but the real problem,
+ 	// IMHO, is not in Mozilla.  Both GTK and Mozilla simply follow
+ 	// well accepted posix C programming conventioin: call
+ 	// setlocale(LC_ALL, "") always.  However, The SL viewer code is
+ 	// written in a style of early 1980 programming, ignoring the fact
+ 	// that there are some cultual conventions other than US's...
+ 	// It's clear we need to somehow handle it, but it is just another
+ 	// story.  The issue I (Alissa) am tackling now is the GtkGLExt
+ 	// and GTK immodule adaptation.  For the moment, I gave up to
+ 	// fix the issue inside SL viewer code, and wrote a quick
+ 	// workaround outside of the program.  See wrapper.sh for details.
+  
+ 	// Yuck, Mozilla's GTK callbacks play with the locale - push/pop
+ 	// the locale to protect it, as exotic/non-C locales
+ 	// causes our code lots of general critical weirdness
+ 	// and crashness. (SL-35450)
+	//	std::string saved_locale = setlocale(LC_ALL, NULL);
+  
+ 	// Handle all pending events.  Should we limit the number of
+ 	// events or elapsed time per an invocation so that the viewer
+ 	// doesn't stop polling network or updating frame?  FIXME.
+ 	while (gtk_events_pending())
+	{
+		gtk_main_iteration();
+	}
+  
+	//	setlocale(LC_ALL, saved_locale.c_str() );
+ 
+	// This is a good time to stop flashing the icon if our mFlashTimer has
+	// expired.
+	if (mFlashing && mFlashTimer.hasExpired())
+	{
+		gtk_window_set_urgency_hint(GTK_WINDOW(mWindow), FALSE);
+		mFlashing = FALSE;
+	}
+}
+  
+gboolean LLWindowSDL::handleGtkMotionNotifyEvent(GdkEventMotion *event)
+{
+ 	const LLCoordWindow window_coord((S32) event->x, (S32) event->y);
+ 	LLCoordGL gl_coord;
+ 	convertCoords(window_coord, &gl_coord);
+ 	const MASK mask = gKeyboard->currentMask(TRUE);
+ 	mCallbacks->handleMouseMove(this, gl_coord, mask);
+ 	return TRUE;
+}
+  
+gboolean LLWindowSDL::handleGtkKeyPressEvent(GdkEventKey *event)
+{
+ 	// *HACK: To make immodule's _reconversion_ feature to work under
+ 	// X11 environment, we need to pass user's text selection to
+ 	// PRIMARY clipboard on some timing before the user presses
+ 	// RECONVERT key.  The natual manner may be to let UI components
+ 	// (LLLineEditor and LLTextEditor, or LLPreeditor from our
+ 	// standing point of view) notify us (LLWindow from their standing
+ 	// point of view) when a selection is made or changed.  Another
+ 	// benefits from the design might be SL viewer being a better
+ 	// GNOME citizen.  However, doing so requires some big changes on
+ 	// LLUI components, and it is only needed on X11.  I don't think
+ 	// it is a good idea to do so.  The following code is an
+ 	// alternative.  Although I have just wrote that we need to pass
+ 	// the selection to clioboard *before* the user presses RECONVERT
+ 	// key, it is technically inaccurate.  The accurate statement is:
+ 	// the selection needs to be passed before the immodule processes
+ 	// a key press event for RECONVERT key.  So, we tries to detect
+ 	// the timing *between* the user presses the RECONVERT key and the
+ 	// immodule processes it.  Let's see what happens.  Also note
+ 	// that, although in principle we should always clear the
+ 	// clipboard when our preeditor has no text selection on it, doing
+ 	// so requires additional communication to X server, causing more
+ 	// type-to-echo delay.  We use a flag,
+ 	// mPrimaryClipboardNeedsReset, to avoid it.
+ 	if (mPreeditor)
+	{
+		S32 position, length;
+		mPreeditor->getSelectionRange(&position, &length);
+		if (length > 0)
 		{
-			mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
+			// We have a selection.  Pass it to the PRIMARY clipboard
+			// so that the immodule can examine it for reconversion.
+			LLWString selection(mPreeditor->getWText(), position, length);
+			std::string selection_utf8 = wstring_to_utf8str(selection);
+			GtkClipboard * const clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+			gtk_clipboard_set_text(clipboard, selection_utf8.c_str(), selection_utf8.length());
+			mPrimaryClipboardNeedsReset = TRUE;
 		}
-                // don't handle mousewheel here...
-
-                break;
-            }
-
-            case SDL_VIDEOEXPOSE:  // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing!
-			    mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h);
-                break;
-
-            case SDL_VIDEORESIZE:  // *FIX: handle this?
-		llinfos << "Handling a resize event: " << event.resize.w <<
-			"x" << event.resize.h << llendl;
-
-		// *FIX: I'm not sure this is necessary!
-		mWindow = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, mSDLFlags);
-		if (!mWindow)
+		else if (mPrimaryClipboardNeedsReset)
 		{
-			// *FIX: More informative dialog?
-			llinfos << "Could not recreate context after resize! Quitting..." << llendl;
-			if(mCallbacks->handleCloseRequest(this))
-    			{
-    				// Get the app to initiate cleanup.
-    				mCallbacks->handleQuit(this);
-    				// The app is responsible for calling destroyWindow when done with GL
-    			}
-                break;
+			GtkClipboard * const clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+			gtk_clipboard_set_text(clipboard, "", 0); // or gtk_clipboard_clear?
+			mPrimaryClipboardNeedsReset = FALSE;
 		}
+	}
+  
+ 	// Pass the event to the GtkIMContext for processing and
+ 	// filtering, if and only if language text input is allowed.  Note
+ 	// that this switching technique is only effective with back end
+ 	// types of input methods, and it doesn't work with Windows IMM or
+ 	// MacOS TSM based immodules.
+ 	if (mPreeditor)
+	{
+		if (gtk_im_context_filter_keypress(mIMContext, event))
+		{
+			return TRUE;
+		}
+	}
+  
+ 	// Process as a raw key down event, if not filtered.
+ 	gKeyboard->handleKeyDown(event->keyval, event->state);
+ 	// Part of the fix for SL-13243.
+ 	if (checkGrabbyKeys(event->keyval, TRUE) != 0)
+	{
+		reallyCaptureInput(TRUE);
+	}
+  
+ 	// Take care of unicode from an unfiltered key stroke.
+ 	const guint32 unicode = gdk_keyval_to_unicode(event->keyval);
+ 	if (unicode)
+	{
+		const MASK mask = gKeyboard->currentMask(FALSE);
+		if ((mask & (MASK_CONTROL | MASK_ALT)) == 0)
+		{
+			// gdk_keyval_to_unicode() doesn't consider Ctrl/Alt keys.
+			// If we produce any "unicode" for those key strokes with
+			// ctrl/alt, the viewer application will be confused.
+			// gdk_keyval_to_unicode() *does* consider Shift keys, so
+			// we don't need to take cre of it.
+			mCallbacks->handleUnicodeChar((llwchar)unicode, mask);
+		}
+	}
+ 	else if ((GDK_Return == event->keyval || GDK_KP_Enter == event->keyval)
+		 && 0 == (event->state & ~GDK_SHIFT_MASK & ~GDK_LOCK_MASK))
+	{
+		// This is a _counter_hack_ against a hack in
+		// LLViewerWindow::handleTranslatedKeyDown(KEY,MASK,BOOL) and
+		// LLViewerWindow::handleUnicodeChar(llwchar,MASK).  The hack
+		// presumes that the RETURN and ENTER keys generate Unicode
+		// U+000D ('\r') when pressed alone, but the
+		// gdk_keyval_to_unicode() doesn't.  We need to supply one by
+		// ourselves.  We allow SHIFT and CAPS LOCK here, because a
+		// user should have intended to type ENTER when he/she hit
+		// ENTER key with one of them.
+		mCallbacks->handleUnicodeChar((llwchar)13, MASK_NONE);
+	}
+	
+ 	return TRUE;
+}
+  
+gboolean LLWindowSDL::handleGtkKeyReleaseEvent(GdkEventKey *event)
+{
+ 	// We need to pass key release events to
+ 	// gtk_im_context_filter_keypress() irrespective of its name.
+ 	if (mPreeditor)
+	{
+		if (gtk_im_context_filter_keypress(mIMContext, event))
+		{
+			return TRUE;
+		}
+	}
+	
+ 	if (checkGrabbyKeys(event->keyval, FALSE) == 0)
+	{
+		reallyCaptureInput(FALSE);
+	}
+	
+ 	// This is a testing hack to pop up a dialog when 4 is released
+ 	// if (event->keyval == '4')
+ 	//OSMessageBox("a whole bunch of text goes right here, whee!  test test test.", "this is the title!", OSMB_YESNO);
+  
+ 	gKeyboard->handleKeyUp(event->keyval, event->state);
+  
+ 	return TRUE;
+}
+  
+gboolean LLWindowSDL::handleGtkButtonPressEvent(GdkEventButton *event)
+{
+ 	// LLUI framework recognizes double clicks only on the left (1st)
+ 	// button.  Those on other buttons are handled as two consequtive
+ 	// single-clicks.  I have a feeling it's wrong; double clicks on
+ 	// other buttons should be handled as *one* single-click, IMHO.
+ 	// For now, however, we follow the way double clicks are handled
+ 	// on other platforms (i.e., Windows/MacOS.)  Gtk recognizes
+ 	// triple clicks but LLUI doesn't.  I believe a tripple should be
+ 	// considered as a double click by ignoring the third click.  For
+ 	// now, however again, we follow the way it is handled on other
+ 	// platforms.
+  
+ 	// A case we *can't* simulate is *four* rapid left clicks.  On
+ 	// Windows, it is recognized as two double clicks.  However, it is
+ 	// not practical to handle the case similarly under Gtk.  Can it
+ 	// make any problem?  I don't think so...
+  
+ 	// The cases for double/triple-clicks are handled on the
+ 	// GDK_BUTTON_PRESS event.  We can just ignore GDK_2BUTTON_PRESS
+ 	// and GDK_3BUTTON_PRESS events.
+ 	if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
+	{
+		return TRUE;
+	}
+  
+ 	// Determine the context.
+ 	const LLCoordWindow window_coord((S32) event->x, (S32) event->y);
+ 	LLCoordGL gl_coord;
+ 	convertCoords(window_coord, &gl_coord);
+ 	MASK mask = gKeyboard->currentMask(TRUE);
+  
+ 	// Flush the remaining language text input if any.
+ 	interruptLanguageTextInput();
+ 
+ 	switch (event->button)
+	{
+	case 1: // The left button
+	{
+		// See if this is a second click of a double-clicking.			
+		BOOL double_click = FALSE;
+		GdkEvent *next_event = gdk_event_peek();
+		if (next_event)
+		{
+			double_click = (next_event->type == GDK_2BUTTON_PRESS);
+			gdk_event_free(next_event);
+		}
+		if (double_click)
+		{
+			mCallbacks->handleDoubleClick(this, gl_coord, mask);
+		}
+		else
+		{
+			mCallbacks->handleMouseDown(this, gl_coord, mask);
+		}
+	}
+	break;
+	
+	case 2: // The middle button
+		mCallbacks->handleMiddleMouseDown(this, gl_coord, mask);
+		break;
 		
-		mCallbacks->handleResize(this, event.resize.w, event.resize.h );
-                break;
-
-            case SDL_ACTIVEEVENT:
-                if (event.active.state & SDL_APPINPUTFOCUS)
-                {
-			// Note that for SDL (particularly on X11), keyboard
-			// and mouse focus are independent things.  Here we are
-			// tracking keyboard focus state changes.
-
-			// We have to do our own state massaging because SDL
-			// can send us two unfocus events in a row for example,
-			// which confuses the focus code [SL-24071].
-			if (event.active.gain != mHaveInputFocus)
-			{
-				if (event.active.gain)
-					mCallbacks->handleFocus(this);
-				else
-					mCallbacks->handleFocusLost(this);
-			
-				mHaveInputFocus = !!event.active.gain;
-			}
-                }
-                if (event.active.state & SDL_APPACTIVE)
-                {
-			// Change in iconification/minimization state.
-			if ((!event.active.gain) != mIsMinimized)
-			{
-				mCallbacks->handleActivate(this, !!event.active.gain);
-				llinfos << "SDL deiconification state switched to " << BOOL(event.active.gain) << llendl;
+	case 3: // The right button
+		mCallbacks->handleRightMouseDown(this, gl_coord, mask);
+		break;
+		
+	default:
+		llinfos << "Ignoring mouse button #" << event->button << " down" << llendl;
+		break;
+	}
 	
-				mIsMinimized = (!event.active.gain);
-			}
-			else
-			{
-				llinfos << "Ignored bogus redundant SDL deiconification state switch to " << BOOL(event.active.gain) << llendl;
-			}
-                }
-                break;
-
-            case SDL_QUIT:
-			    if(mCallbacks->handleCloseRequest(this))
-    			{
-    				// Get the app to initiate cleanup.
-    				mCallbacks->handleQuit(this);
-    				// The app is responsible for calling destroyWindow when done with GL
-    			}
-                break;
+ 	return TRUE;
+}
+  
+gboolean LLWindowSDL::handleGtkButtonReleaseEvent(GdkEventButton *event)
+{
+ 	const LLCoordWindow window_coord((S32) event->x, (S32) event->y);
+ 	LLCoordGL gl_coord;
+ 	convertCoords(window_coord, &gl_coord);
+ 	const MASK mask = gKeyboard->currentMask(TRUE);
+  
+ 	switch (event->button)
+	{
+	case 1: // The left button
+		mCallbacks->handleMouseUp(this, gl_coord, mask);
+		break;
+		
+	case 2: // The middle button
+		mCallbacks->handleMiddleMouseUp(this, gl_coord, mask);
+		break;
+		
+	case 3: // The right button
+		mCallbacks->handleRightMouseUp(this, gl_coord, mask);
+		break;
+		
 	default:
-		//llinfos << "Unhandled SDL event type " << event.type << llendl;
+		llinfos << "Ignoring mouse button #" << event->button << " up" << llendl;
 		break;
-        }
-    }
-
-#if LL_X11
-    // This is a good time to stop flashing the icon if our mFlashTimer has
-    // expired.
-    if (mFlashing && mFlashTimer.hasExpired())
-    {
-	    x11_set_urgent(FALSE);
-	    mFlashing = FALSE;
-    }
-#endif // LL_X11
+	}
+ 	return TRUE;
 }
+ 		
+gboolean LLWindowSDL::handleGtkScrollEvent(GdkEventScroll *event)
+{
+ 	switch (event->direction)
+	{
+	case GDK_SCROLL_UP:
+		mCallbacks->handleScrollWheel(this, -1);
+		break;
+	case GDK_SCROLL_DOWN:
+		mCallbacks->handleScrollWheel(this, 1);
+		break;
+	default:
+		llinfos << "Ignoring scroll wheel #" << event->direction << llendl;
+		break;
+	}
+ 	return TRUE;
+}
 
-static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty)
+gboolean LLWindowSDL::handleGtkFocusInEvent(GdkEventFocus *event)
 {
-	SDL_Cursor *sdlcursor = NULL;
-	SDL_Surface *bmpsurface;
-
-	// Load cursor pixel data from BMP file
-	bmpsurface = Load_BMP_Resource(filename);
-	if (bmpsurface && bmpsurface->w%8==0)
+ 	if (!mWindowIsFocused)
 	{
-		SDL_Surface *cursurface;
-		lldebugs << "Loaded cursor file " << filename << " "
-			 << bmpsurface->w << "x" << bmpsurface->h << llendl;
-		cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE,
-						   bmpsurface->w,
-						   bmpsurface->h,
-						   32,
-						   SDL_SwapLE32(0xFFU),
-						   SDL_SwapLE32(0xFF00U),
-						   SDL_SwapLE32(0xFF0000U),
-						   SDL_SwapLE32(0xFF000000U));
-		SDL_FillRect(cursurface, NULL, SDL_SwapLE32(0x00000000U));
-
-		// Blit the cursor pixel data onto a 32-bit RGBA surface so we
-		// only have to cope with processing one type of pixel format.
-		if (0 == SDL_BlitSurface(bmpsurface, NULL,
-					 cursurface, NULL))
-		{
-			// n.b. we already checked that width is a multiple of 8.
-			const int bitmap_bytes = (cursurface->w * cursurface->h) / 8;
-			unsigned char *cursor_data = new unsigned char[bitmap_bytes];
-			unsigned char *cursor_mask = new unsigned char[bitmap_bytes];
-			memset(cursor_data, 0, bitmap_bytes);
-			memset(cursor_mask, 0, bitmap_bytes);
-			int i,j;
-			// Walk the RGBA cursor pixel data, extracting both data and
-			// mask to build SDL-friendly cursor bitmaps from.  The mask
-			// is inferred by color-keying against 200,200,200
-			for (i=0; i<cursurface->h; ++i) {
-				for (j=0; j<cursurface->w; ++j) {
-					U8 *pixelp =
-						((U8*)cursurface->pixels)
-						+ cursurface->pitch * i
-						+ j*cursurface->format->BytesPerPixel;
-					U8 srcred = pixelp[0];
-					U8 srcgreen = pixelp[1];
-					U8 srcblue = pixelp[2];
-					BOOL mask_bit = (srcred != 200)
-						|| (srcgreen != 200)
-						|| (srcblue != 200);
-					BOOL data_bit = mask_bit && (srcgreen <= 80);//not 0x80
-					unsigned char bit_offset = (cursurface->w/8) * i
-						+ j/8;
-					cursor_data[bit_offset]	|= (data_bit) << (7 - (j&7));
-					cursor_mask[bit_offset]	|= (mask_bit) << (7 - (j&7));
-				}
-			}
-			sdlcursor = SDL_CreateCursor((Uint8*)cursor_data,
-						     (Uint8*)cursor_mask,
-						     cursurface->w, cursurface->h,
-						     hotx, hoty);
-			delete[] cursor_data;
-			delete[] cursor_mask;
-		} else {
-			llwarns << "CURSOR BLIT FAILURE, cursurface: " << cursurface << llendl;
-		}
-		SDL_FreeSurface(cursurface);
-		SDL_FreeSurface(bmpsurface);
-	} else {
-		llwarns << "CURSOR LOAD FAILURE " << filename << llendl;
+		mCallbacks->handleFocus(this);
+		mWindowIsFocused = TRUE;
+		mPrimaryClipboardNeedsReset = TRUE;
+		updateIMFocus();
 	}
-
-	return sdlcursor;
+ 	return FALSE;
 }
+  
+gboolean LLWindowSDL::handleGtkFocusOutEvent(GdkEventFocus *event)
+{
+ 	// SDL code included the following comment here.
+ 	// I believe GTK is more stable on focus in/out matching...
+ 	// 
+ 	// // We have to do our own state massaging because SDL
+ 	// // can send us two unfocus events in a row for example,
+ 	// // which confuses the focus code [SL-24071].
+ 
+ 	if (mWindowIsFocused)
+	{
+		interruptLanguageTextInput();
+		mWindowIsFocused = FALSE;
+		updateIMFocus();
+		mCallbacks->handleFocusLost(this);
+	}
+ 	return FALSE;
+}
 
-void LLWindowSDL::setCursor(ECursorType cursor)
+gboolean LLWindowSDL::handleGtkExposeEvent(GdkEventExpose *event)
 {
-	if (mCurrentCursor != cursor)
+ 	mCallbacks->handlePaint(this, event->area.x, event->area.y, event->area.width, event->area.height);
+ 	return FALSE;
+}
+ 			
+gboolean LLWindowSDL::handleGtkConfigureEvent(GdkEventConfigure *event)
+{
+ 	mCallbacks->handleResize(this, event->width, event->height);
+ 	return FALSE;
+}
+ 
+gboolean LLWindowSDL::handleGtkWindowStateEvent(GdkEventWindowState *event)
+{
+ 	if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED)
 	{
-		if (cursor < UI_CURSOR_COUNT)
-		{
-			SDL_Cursor *sdlcursor = mSDLCursors[cursor];
-			// Try to default to the arrow for any cursors that
-			// did not load correctly.
-			if (!sdlcursor && mSDLCursors[UI_CURSOR_ARROW])
-				sdlcursor = mSDLCursors[UI_CURSOR_ARROW];
-			if (sdlcursor)
-				SDL_SetCursor(sdlcursor);
-		} else {
-			llwarns << "Tried to set invalid cursor number " << cursor << llendl;
-		}
-		mCurrentCursor = cursor;
+		const BOOL active = ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) == 0);
+		mCallbacks->handleActivate(this, active);
 	}
+ 	return FALSE;		
 }
-
+ 
+gboolean LLWindowSDL::handleGtkDeleteEvent(GdkEvent *event)
+{
+ 	if(mCallbacks->handleCloseRequest(this))
+	{
+		// Get the app to initiate cleanup.
+		mCallbacks->handleQuit(this);
+		// The app is responsible for calling destroyWindow when done with GL
+	}
+ 	return TRUE;
+}
+ 
+// Load a BMP file and build a cursor (mouse pointer figure) from it.
+// The BMP file may be greyscale, paletted, or truecolor, but in any
+// case it should not have alpha channel.  It is keyed with
+// <200,200,200> grey, i.e., pixels with that color is converted into
+// 100% transparent pixel.  This specification is simply inherited
+// from the SDL version.  I believe it's far easier to use PNG with
+// alpha in GTK environment.
+ 
+static GdkCursor * make_gdk_cursor_from_bmp(GdkDisplay * display, const char *filename, int x, int y)
+{
+ 	const guchar key_r = 200;
+ 	const guchar key_g = 200;
+ 	const guchar key_b = 200;
+ 
+ 	GdkPixbuf * raw_pixbuf = load_bmp_resource(filename, "cursor");
+ 	if (!raw_pixbuf)
+	{
+		return NULL;
+	}
+	
+ 	GdkPixbuf * pixbuf = gdk_pixbuf_add_alpha(raw_pixbuf, TRUE, key_r, key_g, key_b);
+ 	if (!pixbuf)
+	{
+		g_object_unref(raw_pixbuf);
+		return NULL;
+	}
+	
+ 	GdkCursor * cursor = gdk_cursor_new_from_pixbuf(display, pixbuf, x, y);
+ 	g_object_unref(pixbuf);
+ 	return cursor;
+}
+ 
+#if 0
+ 
+// This is an alternative to make_gdk_cursor_from_bmp() above.  Unlike
+// SDL, gdk-pixbuf provides reading of Windows .CUR files, and we
+// don't need to struggle with bmp files with hard-coded hot spot
+// coordinates or with manual keying (for transparent backgrounds).
+// The problem is, switching from BMP based cursor to CUR based cursor
+// requires re-layout of the resource files in the source tree
+// largely, as well as updates to the build/package scripts.  It is
+// nearly impossible for an open source developer outside LL to do,
+// until LL switches to more open development process...
+ 
+static GdkCursor * make_gdk_cursor(GdkDisplay * display, const char *filename)
+{
+ 	GdkPixbuf * pixbuf = load_bmp_resource(filename, "cursor");
+ 	if (!pixbuf)
+	{
+		return NULL;
+	}
+ 
+ 	// gdk pixbuf supports loading of Windows .CUR files, and sets the
+ 	// hot spot coordinates in "x_hot" and "y_hot" optioins, but gdk
+ 	// cursor package does not recognize these options in gdk
+ 	// pixbuf...  I'm not sure why.  We need to take care of hot spot
+ 	// by ourselves.
+ 
+	const gchar * const x_hot_ptr = gdk_pixbuf_get_option(pixbuf, "x_hot");
+ 	{
+ 		llwarns << "Cursor file " << filename << " has no x_hot" << llendl;
+ 		return NULL;
+ 	}
+ 	const gint64 x_hot = g_ascii_strtoll(x_hot_ptr, NULL, 10);
+ 	if (x_host < 0 || x_hot > gdk_pixbuf_get_width(pixbuf))
+	{
+		llwarns << "Cursor x_host out of range in " << filename << llendl;
+		return NULL;
+	}
+ 
+	const gchar * const y_hot_ptr = gdk_pixbuf_get_option(pixbuf, "y_hot");
+ 	{
+ 		llwarns << "Cursor file " << filename << " has no y_hot" << llendl;
+ 		return NULL;
+ 	}
+ 	const gint64 y_hot = g_ascii_strtoll(y_hot_ptr, NULL, 10);
+ 	if (y_host < 0 || y_hot > gdk_pixbuf_get_width(pixbuf))
+	{
+		llwarns << "Cursor y_host out of range in " << filename << llendl;
+		return NULL;
+	}
+ 
+ 	GdkCursor * cursor = gdk_cursor_new_from_pixbuf(display, pixbuf, (gint) x_hot, (gint) y_hot);
+ 	g_object_unref(pixbuf);
+ 	return cursor;
+}
+ 
+#endif
+ 
+void LLWindowSDL::setCursor(ECursorType cursor_type)
+{
+ 	if (mCurrentCursor == cursor_type)
+	{
+		return;
+	}
+	
+ 	if (cursor_type >= UI_CURSOR_COUNT)
+	{
+		llwarns << "Tried to set invalid cursor number " << cursor_type << llendl;
+		cursor_type = UI_CURSOR_ARROW;
+	}
+ 	mCurrentCursor = cursor_type;
+ 
+ 	if (!mCursorHidden)
+	{
+		gdk_window_set_cursor(mWindow->window, mCursors[cursor_type]);
+	}
+}
+ 
 ECursorType LLWindowSDL::getCursor()
 {
-	return mCurrentCursor;
+  	return mCurrentCursor;
 }
 
 void LLWindowSDL::initCursors()
 {
-	int i;
-	// Blank the cursor pointer array for those we may miss.
-	for (i=0; i<UI_CURSOR_COUNT; ++i)
+  	// Blank the cursor pointer array for those we may miss.
+ 	for (int i=0; i<UI_CURSOR_COUNT; ++i)
 	{
-		mSDLCursors[i] = NULL;
+		mCursors[i] = NULL;
 	}
-	// Pre-make an SDL cursor for each of the known cursor types.
-	// We hardcode the hotspots - to avoid that we'd have to write
-	// a .cur file loader.
-	// NOTE: SDL doesn't load RLE-compressed BMP files.
-	mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0);
-	mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15);
-	mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10);
-	mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16);
-	mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14);
-	mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17);
-	mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17);
-	mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14);
-	mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16);
-	mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
-	mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
-	mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13);
-	mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6);
-	mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5);
-	mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7);
-	mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0);
-	mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0);
-	mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0);
-	mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0);
-	mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8);
-	mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0);
-	mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13);
-	mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5);
-	mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5);
-	mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5);
-	mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLPAY] = makeSDLCursorFromBMP("toolpay.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLPLAY] = makeSDLCursorFromBMP("toolplay.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLPAUSE] = makeSDLCursorFromBMP("toolpause.BMP",0,0);
-	mSDLCursors[UI_CURSOR_TOOLMEDIAOPEN] = makeSDLCursorFromBMP("toolmediaopen.BMP",0,0);
-	mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
+ 
+ 	GdkDisplay * display = gtk_widget_get_display(mWindow);
+ 
+ 	// Pre-make a GDK cursor for each of the known cursor types.
+ 	// We hardcode the hotspots, because SDL had no .CUR loader.
+ 	mCursors[UI_CURSOR_ARROW] = make_gdk_cursor_from_bmp(display, "llarrow.BMP",0,0);
+ 	mCursors[UI_CURSOR_WAIT] = make_gdk_cursor_from_bmp(display, "wait.BMP",12,15);
+ 	mCursors[UI_CURSOR_HAND] = make_gdk_cursor_from_bmp(display, "hand.BMP",7,10);
+ 	mCursors[UI_CURSOR_IBEAM] = make_gdk_cursor_from_bmp(display, "ibeam.BMP",15,16);
+ 	mCursors[UI_CURSOR_CROSS] = make_gdk_cursor_from_bmp(display, "cross.BMP",16,14);
+ 	mCursors[UI_CURSOR_SIZENWSE] = make_gdk_cursor_from_bmp(display, "sizenwse.BMP",14,17);
+ 	mCursors[UI_CURSOR_SIZENESW] = make_gdk_cursor_from_bmp(display, "sizenesw.BMP",17,17);
+ 	mCursors[UI_CURSOR_SIZEWE] = make_gdk_cursor_from_bmp(display, "sizewe.BMP",16,14);
+ 	mCursors[UI_CURSOR_SIZENS] = make_gdk_cursor_from_bmp(display, "sizens.BMP",17,16);
+ 	mCursors[UI_CURSOR_NO] = make_gdk_cursor_from_bmp(display, "llno.BMP",8,8);
+ 	mCursors[UI_CURSOR_WORKING] = make_gdk_cursor_from_bmp(display, "working.BMP",12,15);
+ 	mCursors[UI_CURSOR_TOOLGRAB] = make_gdk_cursor_from_bmp(display, "lltoolgrab.BMP",2,13);
+ 	mCursors[UI_CURSOR_TOOLLAND] = make_gdk_cursor_from_bmp(display, "lltoolland.BMP",1,6);
+ 	mCursors[UI_CURSOR_TOOLFOCUS] = make_gdk_cursor_from_bmp(display, "lltoolfocus.BMP",8,5);
+ 	mCursors[UI_CURSOR_TOOLCREATE] = make_gdk_cursor_from_bmp(display, "lltoolcreate.BMP",7,7);
+ 	mCursors[UI_CURSOR_ARROWDRAG] = make_gdk_cursor_from_bmp(display, "arrowdrag.BMP",0,0);
+ 	mCursors[UI_CURSOR_ARROWCOPY] = make_gdk_cursor_from_bmp(display, "arrowcop.BMP",0,0);
+ 	mCursors[UI_CURSOR_ARROWDRAGMULTI] = make_gdk_cursor_from_bmp(display, "llarrowdragmulti.BMP",0,0);
+ 	mCursors[UI_CURSOR_ARROWCOPYMULTI] = make_gdk_cursor_from_bmp(display, "arrowcopmulti.BMP",0,0);
+ 	mCursors[UI_CURSOR_NOLOCKED] = make_gdk_cursor_from_bmp(display, "llnolocked.BMP",8,8);
+ 	mCursors[UI_CURSOR_ARROWLOCKED] = make_gdk_cursor_from_bmp(display, "llarrowlocked.BMP",0,0);
+ 	mCursors[UI_CURSOR_GRABLOCKED] = make_gdk_cursor_from_bmp(display, "llgrablocked.BMP",2,13);
+ 	mCursors[UI_CURSOR_TOOLTRANSLATE] = make_gdk_cursor_from_bmp(display, "lltooltranslate.BMP",0,0);
+ 	mCursors[UI_CURSOR_TOOLROTATE] = make_gdk_cursor_from_bmp(display, "lltoolrotate.BMP",0,0);
+ 	mCursors[UI_CURSOR_TOOLSCALE] = make_gdk_cursor_from_bmp(display, "lltoolscale.BMP",0,0);
+ 	mCursors[UI_CURSOR_TOOLCAMERA] = make_gdk_cursor_from_bmp(display, "lltoolcamera.BMP",7,5);
+ 	mCursors[UI_CURSOR_TOOLPAN] = make_gdk_cursor_from_bmp(display, "lltoolpan.BMP",7,5);
+ 	mCursors[UI_CURSOR_TOOLZOOMIN] = make_gdk_cursor_from_bmp(display, "lltoolzoomin.BMP",7,5);
+ 	mCursors[UI_CURSOR_TOOLPICKOBJECT3] = make_gdk_cursor_from_bmp(display, "toolpickobject3.BMP",0,0);
+ 	mCursors[UI_CURSOR_TOOLSIT] = make_gdk_cursor_from_bmp(display, "toolsit.BMP",0,0);
+ 	mCursors[UI_CURSOR_TOOLBUY] = make_gdk_cursor_from_bmp(display, "toolbuy.BMP",0,0);
+ 	mCursors[UI_CURSOR_TOOLPAY] = make_gdk_cursor_from_bmp(display, "toolpay.BMP",0,0);
+ 	mCursors[UI_CURSOR_TOOLOPEN] = make_gdk_cursor_from_bmp(display, "toolopen.BMP",0,0);
+ 	mCursors[UI_CURSOR_PIPETTE] = make_gdk_cursor_from_bmp(display, "lltoolpipette.BMP",2,28);
+ 
+ 	// Substitute any missing cursor with a default, so that we don't
+ 	// need to test the case on-the-fly.  The good-old X default
+ 	// cursor is used, as opposed to UI_CURSOR_ARROW, so that QAs can
+ 	// notice the case easily.
+ 	GdkCursor * default_cursor = gdk_cursor_new_for_display(display, GDK_X_CURSOR);
+ 	for (int i = 0; i < UI_CURSOR_COUNT; i++)
+	{
+		if (!mCursors[i])
+		{
+			gdk_cursor_ref(default_cursor);
+			mCursors[i] = default_cursor;
+		}
+	}
+ 	gdk_cursor_unref(default_cursor);
+ 
+ 	// Prepare for a _hidden_ cursor.  In GTK, mouse cursor is always
+ 	// shown, so we need a full transparent cursor to simulate
+ 	// show/hide behaviour on other platforms.
+ 	// 
+ 	// I'm sorry to use a lot of magic constants here.  I'm not so
+ 	// enthusiastic to define appropriate constant names for this
+ 	// trivial task... -- Alissa
+ 
+ 	static gchar bits[32];
+ 	static GdkColor color;
+ 	GdkPixmap * pixmap = gdk_bitmap_create_from_data(NULL, bits, 16, 16);
+ 	mHiddenCursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &color, &color, 8, 8);
+ 	gdk_pixmap_unref(pixmap);
 }
-
+  
 void LLWindowSDL::quitCursors()
 {
-	int i;
-	if (mWindow)
+ 	for (int i = 0; i < UI_CURSOR_COUNT; i++)
 	{
-		for (i=0; i<UI_CURSOR_COUNT; ++i)
+		if (mCursors[i])
 		{
-			if (mSDLCursors[i])
-			{
-				SDL_FreeCursor(mSDLCursors[i]);
-				mSDLCursors[i] = NULL;
-			}
+			gdk_cursor_unref(mCursors[i]);
+			mCursors[i] = NULL;
 		}
-	} else {
-		// SDL doesn't refcount cursors, so if the window has
-		// already been destroyed then the cursors have gone with it.
-		llinfos << "Skipping quitCursors: mWindow already gone." << llendl;
-		for (i=0; i<UI_CURSOR_COUNT; ++i)
-			mSDLCursors[i] = NULL;
 	}
+	
+ 	if (mHiddenCursor)
+	{
+		gdk_cursor_unref(mHiddenCursor);
+		mHiddenCursor = NULL;
+	}
 }
-
+  
 void LLWindowSDL::captureMouse()
 {
-	// SDL already enforces the semantics that captureMouse is
-	// used for, i.e. that we continue to get mouse events as long
-	// as a button is down regardless of whether we left the
-	// window, and in a less obnoxious way than SDL_WM_GrabInput
-	// which would confine the cursor to the window too.
-
-	//llinfos << "LLWindowSDL::captureMouse" << llendl;
+ 	// We always request GDK_POINTER_MOTION_MASK and GDK_BUTTON_MOTION_MASK with
+ 	// gtk_widget_add_events(), so the captureMouse() semantics needed
+ 	// by SL viewer is automatic.
 }
-
+  
 void LLWindowSDL::releaseMouse()
 {
-	// see LWindowSDL::captureMouse()
-	
-	//llinfos << "LLWindowSDL::releaseMouse" << llendl;
+  	// see LWindowSDL::captureMouse()
 }
-
+  	
+BOOL LLWindowSDL::isCursorHidden()
+{
+ 	return mCursorHidden;
+}
+  
 void LLWindowSDL::hideCursor()
 {
-	if(!mCursorHidden)
+  	if(!mCursorHidden)
 	{
-		// llinfos << "hideCursor: hiding" << llendl;
 		mCursorHidden = TRUE;
 		mHideCursorPermanent = TRUE;
-		SDL_ShowCursor(0);
+		gdk_window_set_cursor(mWindow->window, mHiddenCursor);
 	}
-	else
-	{
-		// llinfos << "hideCursor: already hidden" << llendl;
-	}
-
-	adjustCursorDecouple();
 }
-
+  
 void LLWindowSDL::showCursor()
 {
-	if(mCursorHidden)
+  	if(mCursorHidden)
 	{
-		// llinfos << "showCursor: showing" << llendl;
 		mCursorHidden = FALSE;
 		mHideCursorPermanent = FALSE;
-		SDL_ShowCursor(1);
+		gdk_window_set_cursor(mWindow->window, mCursors[mCurrentCursor]);
 	}
-	else
-	{
-		// llinfos << "showCursor: already visible" << llendl;
-	}
-
-	adjustCursorDecouple();
 }
-
+  
 void LLWindowSDL::showCursorFromMouseMove()
 {
 	if (!mHideCursorPermanent)
@@ -2466,12 +2344,11 @@
 	if (!mHideCursorPermanent)
 	{
 		hideCursor();
+		// hideCursor turns mHideCursorPermanent TRUE!
 		mHideCursorPermanent = FALSE;
 	}
 }
 
-
-
 //
 // LLSplashScreenSDL - I don't think we'll bother to implement this; it's
 // fairly obsolete at this point.
@@ -2496,9 +2373,6 @@
 {
 }
 
-
-
-#if LL_GTK
 static void response_callback (GtkDialog *dialog,
 			       gint       arg1,
 			       gpointer   user_data)
@@ -2515,100 +2389,60 @@
 
 	ll_try_gtk_init();
 
-	if(gWindowImplementation != NULL)
-		gWindowImplementation->beforeDialog();
-
-	if (ll_try_gtk_init()
-	    // We can NOT expect to combine GTK and SDL's aggressive fullscreen
-	    && ((NULL==gWindowImplementation) || (!was_fullscreen))
-	    )
+	GtkWidget *win = NULL;
+	GtkWindow * parent = gWindowImplementation ? GTK_WINDOW(gWindowImplementation->getGtkWindow()) : NULL;
+	GtkDialogFlags flags = GTK_DIALOG_MODAL;
+	GtkMessageType messagetype;
+	GtkButtonsType buttons;
+	switch (type)
 	{
-		GtkWidget *win = NULL;
+	default:
+	case OSMB_OK:
+		messagetype = GTK_MESSAGE_WARNING;
+		buttons = GTK_BUTTONS_OK;
+		break;
+	case OSMB_OKCANCEL:
+		messagetype = GTK_MESSAGE_QUESTION;
+		buttons = GTK_BUTTONS_OK_CANCEL;
+		break;
+	case OSMB_YESNO:
+		messagetype = GTK_MESSAGE_QUESTION;
+		buttons = GTK_BUTTONS_YES_NO;
+		break;
+	}
+	win = gtk_message_dialog_new(parent, flags, messagetype, buttons, text);
 
-		llinfos << "Creating a dialog because we're in windowed mode and GTK is happy." << llendl;
-		
-		GtkDialogFlags flags = GTK_DIALOG_MODAL;
-		GtkMessageType messagetype;
-		GtkButtonsType buttons;
-		switch (type)
-		{
-		default:
-		case OSMB_OK:
-			messagetype = GTK_MESSAGE_WARNING;
-			buttons = GTK_BUTTONS_OK;
-			break;
-		case OSMB_OKCANCEL:
-			messagetype = GTK_MESSAGE_QUESTION;
-			buttons = GTK_BUTTONS_OK_CANCEL;
-			break;
-		case OSMB_YESNO:
-			messagetype = GTK_MESSAGE_QUESTION;
-			buttons = GTK_BUTTONS_YES_NO;
-			break;
-		}
-		win = gtk_message_dialog_new(NULL,
-                                             flags, messagetype, buttons,
-                                             text);
+	gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);
 
-# if LL_X11
-		// Make GTK tell the window manager to associate this
-		// dialog with our non-GTK SDL window, which should try
-		// to keep it on top etc.
-		if (gWindowImplementation &&
-		    gWindowImplementation->mSDL_XWindowID != None)
-		{
-			gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
-			GdkWindow *gdkwin = gdk_window_foreign_new(gWindowImplementation->mSDL_XWindowID);
-			gdk_window_set_transient_for(GTK_WIDGET(win)->window,
-						     gdkwin);
-		}
-# endif //LL_X11
+	gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DIALOG);
 
-		gtk_window_set_position(GTK_WINDOW(win),
-					GTK_WIN_POS_CENTER_ON_PARENT);
+	if (caption)
+	{
+		gtk_window_set_title(GTK_WINDOW(win), caption);
+	}
 
-		gtk_window_set_type_hint(GTK_WINDOW(win),
-					 GDK_WINDOW_TYPE_HINT_DIALOG);
+	gint response = GTK_RESPONSE_NONE;
+	g_signal_connect(win, "response", G_CALLBACK(response_callback), &response);
 
-		if (caption)
-			gtk_window_set_title(GTK_WINDOW(win), caption);
+	// we should be able to use a gtk_dialog_run(), but it's
+	// apparently not written to exist in a world without a higher
+	// gtk_main(), so we manage its signal/destruction outselves.
+	gtk_widget_show_all (win);
+	gtk_main();
 
-		gint response = GTK_RESPONSE_NONE;
-		g_signal_connect (win,
-				  "response", 
-				  G_CALLBACK (response_callback),
-				  &response);
-
-		// we should be able to use a gtk_dialog_run(), but it's
-		// apparently not written to exist in a world without a higher
-		// gtk_main(), so we manage its signal/destruction outselves.
-		gtk_widget_show_all (win);
-		gtk_main();
-
-		//llinfos << "response: " << response << llendl;
-		switch (response)
-		{
-		case GTK_RESPONSE_OK:     rtn = OSBTN_OK; break;
-		case GTK_RESPONSE_YES:    rtn = OSBTN_YES; break;
-		case GTK_RESPONSE_NO:     rtn = OSBTN_NO; break;
-		case GTK_RESPONSE_APPLY:  rtn = OSBTN_OK; break;
-		case GTK_RESPONSE_NONE:
-		case GTK_RESPONSE_CANCEL:
-		case GTK_RESPONSE_CLOSE:
-		case GTK_RESPONSE_DELETE_EVENT:
-		default: rtn = OSBTN_CANCEL;
-		}
-	}
-	else
+	switch (response)
 	{
-		llinfos << "MSGBOX: " << caption << ": " << text << llendl;
-		llinfos << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << llendl;
-		rtn = OSBTN_OK;
+	case GTK_RESPONSE_OK:     rtn = OSBTN_OK; break;
+	case GTK_RESPONSE_YES:    rtn = OSBTN_YES; break;
+	case GTK_RESPONSE_NO:     rtn = OSBTN_NO; break;
+	case GTK_RESPONSE_APPLY:  rtn = OSBTN_OK; break;
+	case GTK_RESPONSE_NONE:
+	case GTK_RESPONSE_CANCEL:
+	case GTK_RESPONSE_CLOSE:
+	case GTK_RESPONSE_DELETE_EVENT:
+	default: rtn = OSBTN_CANCEL;
 	}
-
-	if(gWindowImplementation != NULL)
-		gWindowImplementation->afterDialog();
-
+	
 	return rtn;
 }
 
@@ -2621,186 +2455,455 @@
 	gtk_color_selection_get_current_color(colorsel, colorp);
 }
 
-BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+
+BOOL LLWindowSDL::dialog_color_picker(F32 *r, F32 *g, F32 *b)
 {
-	BOOL rtn = FALSE;
-
-	beforeDialog();
-
-	if (ll_try_gtk_init()
-	    // We can NOT expect to combine GTK and SDL's aggressive fullscreen
-	    && !was_fullscreen
-	    )
+  	BOOL rtn = FALSE;
+  
+  	beforeDialog();
+  
+ 	GtkWidget *win = gtk_color_selection_dialog_new(NULL);
+ 	gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(getGtkWindow()));
+ 
+ 	GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel);
+ 
+ 	GdkColor color, orig_color;
+ 	orig_color.red = guint16(65535 * *r);
+ 	orig_color.green= guint16(65535 * *g);
+ 	orig_color.blue = guint16(65535 * *b);
+ 	color = orig_color;
+ 
+ 	gtk_color_selection_set_previous_color(colorsel, &color);
+ 	gtk_color_selection_set_current_color(colorsel, &color);
+ 	gtk_color_selection_set_has_palette(colorsel, TRUE);
+ 	gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
+ 
+ 	gint response = GTK_RESPONSE_NONE;
+ 	g_signal_connect(win, "response", G_CALLBACK(response_callback), &response);
+ 	g_signal_connect (G_OBJECT (colorsel), "color_changed", G_CALLBACK(color_changed_callback), &color);
+ 
+ 	gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+ 	gtk_widget_show_all(win);
+ 	// hide the help button - we don't service it.
+ 	gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button);
+ 	gtk_main();
+ 
+ 	if (response == GTK_RESPONSE_OK &&
+	    (orig_color.red != color.red
+	     || orig_color.green != color.green
+	     || orig_color.blue != color.blue) )
 	{
-		GtkWidget *win = NULL;
-
-		win = gtk_color_selection_dialog_new(NULL);
-
-# if LL_X11
-		// Get GTK to tell the window manager to associate this
-		// dialog with our non-GTK SDL window, which should try
-		// to keep it on top etc.
-		if (mSDL_XWindowID != None)
-		{
-			gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
-			GdkWindow *gdkwin = gdk_window_foreign_new(mSDL_XWindowID);
-			gdk_window_set_transient_for(GTK_WIDGET(win)->window,
-						     gdkwin);
-		}
-# endif //LL_X11
-
-		GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel);
-
-		GdkColor color, orig_color;
-		orig_color.red = guint16(65535 * *r);
-		orig_color.green= guint16(65535 * *g);
-		orig_color.blue = guint16(65535 * *b);
-		color = orig_color;
-
-		gtk_color_selection_set_previous_color (colorsel, &color);
-		gtk_color_selection_set_current_color (colorsel, &color);
-		gtk_color_selection_set_has_palette (colorsel, TRUE);
-		gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
-
-		gint response = GTK_RESPONSE_NONE;
-		g_signal_connect (win,
-				  "response", 
-				  G_CALLBACK (response_callback),
-				  &response);
-
-		g_signal_connect (G_OBJECT (colorsel), "color_changed",
-				  G_CALLBACK (color_changed_callback),
-				  &color);
-
-		gtk_window_set_modal(GTK_WINDOW(win), TRUE);
-		gtk_widget_show_all(win);
-		// hide the help button - we don't service it.
-		gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button);
-		gtk_main();
-
-		if (response == GTK_RESPONSE_OK &&
-		    (orig_color.red != color.red
-		     || orig_color.green != color.green
-		     || orig_color.blue != color.blue) )
-		{
-			*r = color.red / 65535.0f;
-			*g = color.green / 65535.0f;
-			*b = color.blue / 65535.0f;
-			rtn = TRUE;
-		}
+		*r = color.red / 65535.0f;
+		*g = color.green / 65535.0f;
+		*b = color.blue / 65535.0f;
+		rtn = TRUE;
 	}
-
-	afterDialog();
-
-	return rtn;
+	
+ 	afterDialog();
+  
+ 	return rtn;
 }
-#else
-S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
+  
+// Open a URL with the user's default web browser.
+// Must begin with protocol identifier.
+void spawn_web_browser(const char* escaped_url)
 {
-	llinfos << "MSGBOX: " << caption << ": " << text << llendl;
-	return 0;
+ 	llinfos << "spawn_web_browser: " << escaped_url << llendl;
+ 	
+ 	// Just in case - before forking.
+ 	gdk_flush();
+  
+ 	const std::string cmd
+ 		= gDirUtilp->getAppRODataDir()
+ 		+ gDirUtilp->getDirDelimiter()
+ 		+ "launch_url.sh";
+ 	gchar * argv[3];
+ 	argv[0] = const_cast<char *>(cmd.c_str());
+ 	argv[1] = const_cast<char *>(escaped_url);
+ 	argv[2] = NULL;
+  
+ 	GError * error = NULL;
+ 	gboolean success = g_spawn_async(NULL, argv, NULL, (GSpawnFlags)0, NULL, NULL, NULL, &error);
+ 	if (!success)
+	{
+		llwarns << "failure on spawning web browser: " << error->message << llendl;
+		g_error_free(error);
+	}
 }
-
-BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+  
+void shell_open( const char* file_path )
 {
-	return (FALSE);
+ 	// *TODO: This function is deprecated and should probably go away.
+ 	llwarns << "Deprecated shell_open(): " << file_path << llendl;
 }
-#endif // LL_GTK
-
-// Open a URL with the user's default web browser.
-// Must begin with protocol identifier.
-void spawn_web_browser(const char* escaped_url)
+  
+void *LLWindowSDL::getPlatformWindow()
 {
-	llinfos << "spawn_web_browser: " << escaped_url << llendl;
+ 	// Unlike SDL version, our main window (mWindow) is now a genuine
+ 	// GTK window.  However, the _platform_window_ that this method
+ 	// returns is used as a container for a hidden mozilla GtkWidget,
+ 	// and mWindow has no more space for another child...  We need to
+ 	// create another container for the purpose.  The window we
+ 	// creates here will *never* be destroyed, as in SDL version and
+ 	// MacOS version.
+  
+ 	GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ 	gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
+ 	gtk_widget_realize(win);
+ 	return win;
+}
+  
+void LLWindowSDL::bringToFront()
+{
+ 	gtk_window_present(GTK_WINDOW(mWindow));
+}
+ 
+// Handling of input methods through GTK im module.
+ 
+void LLWindowSDL::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)
+{
+ 	if (preeditor == NULL && b)
+	{
+		llwarns << "allowLanguageTextInput(NULL, TRUE) ... This call is prohibited!" << llendl;
+		return;
+	}
+  
+ 	if (preeditor != mPreeditor && !b)
+	{
+		return;
+	}
 	
-#if LL_LINUX || LL_SOLARIS
-# if LL_X11
-	if (gWindowImplementation && gWindowImplementation->mSDL_Display)
+ 	// Take care of old and new preeditors.
+ 	if (preeditor != mPreeditor || !b)
 	{
-		maybe_lock_display();
-		// Just in case - before forking.
-		XSync(gWindowImplementation->mSDL_Display, False);
-		maybe_unlock_display();
+		// Needs to interrupt before updating mPreeditor so that any
+		// forced commit occures on the old preeditor.
+		interruptLanguageTextInput();
+		mPreeditor = (b ? preeditor : NULL);
+		mPrimaryClipboardNeedsReset = TRUE;
+		updateIMFocus();
 	}
-# endif // LL_X11
-
-	std::string cmd;
-	cmd  = gDirUtilp->getAppRODataDir().c_str();
-	cmd += gDirUtilp->getDirDelimiter().c_str();
-	cmd += "launch_url.sh";
-	char* const argv[] = {(char*)cmd.c_str(), (char*)escaped_url, NULL};
-
-	fflush(NULL);
-	pid_t pid = fork();
-	if (pid == 0)
-	{ // child
-		// disconnect from stdin/stdout/stderr, or child will
-		// keep our output pipe undesirably alive if it outlives us.
-		close(0);
-		close(1);
-		close(2);
-		// end ourself by running the command
-		execv(cmd.c_str(), argv);	/* Flawfinder: ignore */
-		// if execv returns at all, there was a problem.
-		llwarns << "execv failure when trying to start " << cmd << llendl;
-		_exit(1); // _exit because we don't want atexit() clean-up!
-	} else {
-		if (pid > 0)
+}
+ 
+// Reset the IM context and discard any remnant preedits both from UI
+// (preeditor) and from our own internal storage.
+ 
+void LLWindowSDL::interruptLanguageTextInput()
+{
+ 	gtk_im_context_reset(mIMContext);
+ 	if (mPreeditText.length() > 0)
+	{
+		mPreeditText.clear();
+		mPreeditSegmentLengths.clear();
+		mPreeditStandouts.clear();
+		mPreeditCaret = 0;
+	}
+ 	if (mPreeditor)
+	{
+		mPreeditor->resetPreedit();
+		mPreeditor->updatePreedit(mPreeditText, mPreeditSegmentLengths, mPreeditStandouts, mPreeditCaret);
+	}
+}
+  
+void LLWindowSDL::updateLanguageTextInputArea()
+{
+ 	if (!mPreeditor)
+	{
+		return;
+	}
+ 
+ 	LLCoordGL caret_coord_gl;
+ 	LLRect preedit_bounds_gl;
+ 	if (!mPreeditor->getPreeditLocation(-1, &caret_coord_gl, &preedit_bounds_gl, NULL))
+	{
+		return;
+	}
+ 
+ 	// We assume the cursor (caret) rect is 2 pixels wide and as high
+ 	// as the preedit bounds.  This is to mimic the behaviour of
+ 	// GtkEntry widget where all immodule should support.
+ 
+ 	const LLCoordGL cursor_top_left_gl(caret_coord_gl.mX, preedit_bounds_gl.mTop);
+ 	LLCoordWindow cursor_top_left_window;
+ 	convertCoords(cursor_top_left_gl, &cursor_top_left_window);
+ 
+ 	const LLCoordGL cursor_bottom_right_gl(caret_coord_gl.mX, preedit_bounds_gl.mBottom);
+ 	LLCoordWindow cursor_bottom_right_window;
+ 	convertCoords(cursor_bottom_right_gl, &cursor_bottom_right_window);
+ 
+ 	GdkRectangle rectangle;
+ 	rectangle.x = cursor_top_left_window.mX;
+ 	rectangle.y = cursor_top_left_window.mY;
+ 	rectangle.width = 2;
+ 	rectangle.height = cursor_bottom_right_window.mY - cursor_top_left_window.mY + 1;
+ 
+ 	gtk_im_context_set_cursor_location(mIMContext, &rectangle);
+}
+  
+void LLWindowSDL::handleGtkCommitSignal(const gchar *committed_text)
+{
+ 	if (!committed_text || !*committed_text)
+	{
+		return;
+	}
+  
+ 	if (mPreeditor)
+	{
+		// Pass the committed characters on-by-one to the preeditor,
+		// preserving the current preedit.
+		mPreeditor->resetPreedit();
+		const LLWString text = utf8str_to_wstring(committed_text);
+		for (LLWString::const_iterator i = text.begin(); i != text.end(); i++)
 		{
-			// parent - wait for child to die
-			int childExitStatus;
-			waitpid(pid, &childExitStatus, 0);
-		} else {
-			llwarns << "fork failure." << llendl;
+			mPreeditor->handleUnicodeCharHere(*i, FALSE);
 		}
+		mPreeditor->updatePreedit(mPreeditText, mPreeditSegmentLengths, mPreeditStandouts, mPreeditCaret);
+		updateLanguageTextInputArea();
 	}
-#endif // LL_LINUX || LL_SOLARIS
-
-	llinfos << "spawn_web_browser returning." << llendl;
+ 	else
+	{
+		// Since we don't pass key press/release events to IM context
+		// without preeditor, it should never emit a commit signal.
+		// The following _fallback_code_ should never be executed.
+		llwarns << "commit signal with no preeditor." << llendl;		
+		
+		// We have no active preeditor, so fallback to the default
+		// behaviour and pass the characters to the application
+		// callback.  We only invoke handleUnicodeChar, since this is
+		// purely a _text_ event, and not related to key
+		// pressing/releasing as it is.
+		const MASK mask = gKeyboard->currentMask(FALSE);
+		const LLWString text = utf8str_to_wstring(committed_text);
+		for (LLWString::const_iterator i = text.begin(); i != text.end(); i++)
+		{
+			mCallbacks->handleUnicodeChar(*i, mask);
+		}
+	}
 }
-
-
-void *LLWindowSDL::getPlatformWindow()
+  
+void LLWindowSDL::handleGtkPreeditChangedSignal()
 {
-#if LL_GTK && LL_LLMOZLIB_ENABLED
-	if (ll_try_gtk_init())
+ 	// Extract preedit from IM.
+ 	gchar *raw_string = NULL;		// in UTF-8
+ 	PangoAttrList *attr_list = NULL;
+ 	gint cursor_pos = 0;			// in _characters_
+ 	gtk_im_context_get_preedit_string(mIMContext, &raw_string, &attr_list, &cursor_pos);
+ 
+ 	// Turn the preedit into our own representation.
+ 	if (!raw_string || !*raw_string)
 	{
-		maybe_lock_display();
-
-		GtkWidget *owin = gtk_window_new(GTK_WINDOW_POPUP);
-		// Why a layout widget?  A MozContainer would be ideal, but
-		// it involves exposing Mozilla headers to mozlib-using apps.
-		// A layout widget with a GtkWindow parent has the desired
-		// properties of being plain GTK, having a window, and being
-		// derived from a GtkContainer.
-		GtkWidget *rtnw = gtk_layout_new(NULL, NULL);
-		gtk_container_add(GTK_CONTAINER(owin), rtnw);
-		gtk_widget_realize(rtnw);
-		GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(rtnw), GTK_NO_WINDOW);
+		// Preedit has been flushed.
+		mPreeditText.clear();
+		mPreeditSegmentLengths.clear();
+		mPreeditStandouts.clear();
+		mPreeditCaret = 0;
+	}
+ 	else
+	{
+		mPreeditText = utf8str_to_wstring(raw_string);
 		
-		maybe_unlock_display();
+		// Map one Pango attribute item to one clause segment.
+		// Consider a range with a BACKGROUND attribute a standout.
+		// Yes, this is hacky, but it works reasonably well on *all*
+		// immodules that I know of without per-immodule switching.
+		// -- Alissa
 		
-		return rtnw;
+		const PangoAttrType standout = PANGO_ATTR_BACKGROUND;
+		const S32 string_length = strlen(raw_string);
+		mPreeditSegmentLengths.clear();
+		mPreeditStandouts.clear();
+		gint last = 0;
+		PangoAttrIterator * itor = pango_attr_list_get_iterator(attr_list);
+		do
+		{
+			// It seems that some immodules may return a strange
+			// PangoAttrList, e.g., exceeding the preedit string
+			// length or having several _gaps_.  We need to be careful
+			// to live symbiotically with them.
+			gint start, end;
+			pango_attr_iterator_range(itor, &start, &end);
+			if (start >= string_length)
+			{
+				break;
+			}
+			if (end > string_length)
+			{
+				end = string_length;
+			}
+			if (start > last)
+			{
+				mPreeditSegmentLengths.push_back(g_utf8_strlen(raw_string + last, start - last));
+				mPreeditStandouts.push_back(FALSE);
+			}
+			if (end > start)
+			{
+				mPreeditSegmentLengths.push_back(g_utf8_strlen(raw_string + start, end - start));
+				mPreeditStandouts.push_back(pango_attr_iterator_get(itor, standout) != NULL);
+			}
+			last = end;
+		} while (pango_attr_iterator_next(itor));
+		pango_attr_iterator_destroy(itor);
+		if (last < string_length)
+		{
+			mPreeditSegmentLengths.push_back(g_utf8_strlen(raw_string + last, -1));
+			mPreeditStandouts.push_back(FALSE);
+		}
+		
+		// cursor_pos is defined to be in _characters_ (as opposed to
+		// UTF-8 bytes), so we can just use it.  Hope all immodule
+		// developers know what is *a* character in Unicode environments...
+		mPreeditCaret = cursor_pos;
 	}
-#endif // LL_GTK && LL_LLMOZLIB_ENABLED
-	// Unixoid mozilla really needs GTK.
-	return NULL;
+	
+ 	// Don't forget to release GTK resources.
+ 	if (raw_string)
+	{
+		g_free(raw_string);
+	}
+ 	if (attr_list)
+	{
+		pango_attr_list_unref(attr_list);
+	}	
+	
+ 	// Show it on the preeditor.  I don't think preedit-changed signal
+ 	// is emitted when we have no preeditor, because we don't pass key
+ 	// events to IM context in the case.  The condition is just a
+ 	// safe guard.
+ 	if (mPreeditor)
+	{
+		mPreeditor->resetPreedit();
+		mPreeditor->updatePreedit(mPreeditText, mPreeditSegmentLengths, mPreeditStandouts, mPreeditCaret);
+	}
+ 	else
+	{
+		llwarns << "preedit-changed signal with no preeditor." << llendl;
+	}
+	
+ 	// Text caret should have been moved, so tell the new location to IM.
+ 	updateLanguageTextInputArea();
 }
-
-void LLWindowSDL::bringToFront()
+  
+void LLWindowSDL::handleGtkRetrieveSurroundingSignal()
 {
-	// This is currently used when we are 'launched' to a specific
-	// map position externally.
-	llinfos << "bringToFront" << llendl;
-#if LL_X11
-	if (mSDL_Display && !mFullscreen)
+ 	if (!mPreeditor)
 	{
-		maybe_lock_display();
-		XRaiseWindow(mSDL_Display, mSDL_XWindowID);
-		XSync(mSDL_Display, False);
-		maybe_unlock_display();
+		llwarns << "retrieve-srrounding signal with no preeditor." << llendl;
+		return;
 	}
-#endif // LL_X11
+	
+ 	S32 preedit_start, preedit_length;
+ 	mPreeditor->getPreeditRange(&preedit_start, &preedit_length);
+ 	const S32 preedit_end = preedit_start + preedit_length;
+ 	const LLWString & text = mPreeditor->getWText();
+  
+ 	// Find a suitable _surrounding_context_.
+ 	S32 end = text.find((llwchar) '\n', preedit_end);
+ 	if (end == LLWString::npos)
+	{
+		end = text.length();
+	}
+ 	S32 start = text.rfind((llwchar) '\n', preedit_start - 1);
+ 	if (start == LLWString::npos)
+	{
+		start = 0;
+	}
+ 	else
+	{
+		start++;
+	}
+ 
+ 	// Combine the contexts before and after the preedit string.  The
+ 	// context string to set should not contain uncommitted preedit
+ 	// text.  We also need to convert the string into utf-8.
+ 	std::string context = wstring_to_utf8str(LLWString(text, start, preedit_start - start));
+ 	const gint cursor_index = context.length();
+ 	context += wstring_to_utf8str(LLWString(text, preedit_end, end - preedit_end));
+ 
+ 	gtk_im_context_set_surrounding(mIMContext, context.c_str(), (gint) context.length(), cursor_index);
 }
-
-#endif // LL_SDL
+  
+gboolean LLWindowSDL::handleGtkDeleteSurroundingSignal(gint offset, gint n_chars)
+{
+ 	if (!mPreeditor)
+	{
+		llwarns << "delete-surrounding signal with no preeditor." << llendl;
+		return FALSE;
+	}
+ 
+ 	// To be honest, I'me having a feeling that the LLPreeditor
+ 	// interface is biased toward Windows IMM API and not suitable for
+ 	// us.  We needed some trick to implement this handler on top of
+ 	// it.  It may be better to revise the LLPreditor methods.  FIXME.
+ 
+ 	mPreeditor->resetPreedit();
+ 	S32 cursor, unused;
+ 	mPreeditor->getPreeditRange(&cursor, &unused);
+ 	const S32 from = llmax(0, cursor + offset);
+ 	const S32 to = llmin(cursor + offset + n_chars, (S32) mPreeditor->getWText().length());
+ 	mPreeditor->markAsPreedit(from, to - from);
+ 	mPreeditor->resetPreedit();
+ 	mPreeditor->updatePreedit(mPreeditText, mPreeditSegmentLengths, mPreeditStandouts, mPreeditCaret);
+ 	updateLanguageTextInputArea();
+ 
+ 	return TRUE;
+}
+  
+// More thunks for IM context signal handlers.
+ 
+static void handleGtkCommitSignal_cb(GtkIMContext *context, gchar *committed_text, gpointer user_data)
+{
+ 	static_cast<LLWindowSDL *>(user_data)->handleGtkCommitSignal(committed_text);
+}
+ 
+static void handleGtkPreeditChangedSignal_cb(GtkIMContext *imcontext, gpointer user_data)
+{
+ 	static_cast<LLWindowSDL *>(user_data)->handleGtkPreeditChangedSignal();
+}
+ 
+static void handleGtkRetrieveSurroundingSignal_cb(GtkIMContext *imcontext, gpointer user_data)
+{
+ 	static_cast<LLWindowSDL *>(user_data)->handleGtkRetrieveSurroundingSignal();
+}
+ 
+static gboolean handleGtkDeleteSurroundingSignal_cb(GtkIMContext *context, gint offset, gint n_chars, gpointer user_data)
+{
+ 	return static_cast<LLWindowSDL *>(user_data)->handleGtkDeleteSurroundingSignal(offset, n_chars);
+}
+ 
+void LLWindowSDL::createIMContext()
+{
+ 	mIMContext = gtk_im_multicontext_new();
+ 	g_signal_connect(G_OBJECT(mIMContext), "commit",               G_CALLBACK(handleGtkCommitSignal_cb),              this);
+ 	g_signal_connect(G_OBJECT(mIMContext), "preedit-changed",      G_CALLBACK(handleGtkPreeditChangedSignal_cb),      this);
+ 	g_signal_connect(G_OBJECT(mIMContext), "retrieve-surrounding", G_CALLBACK(handleGtkRetrieveSurroundingSignal_cb), this);
+ 	g_signal_connect(G_OBJECT(mIMContext), "delete-surrounding",   G_CALLBACK(handleGtkDeleteSurroundingSignal_cb),   this);
+ 
+ 	gtk_im_context_set_client_window(mIMContext, mWindow->window);
+}
+ 
+void LLWindowSDL::destroyIMContext()
+{
+ 	gtk_im_context_set_client_window(mIMContext, NULL);
+ 	g_object_unref(mIMContext);
+}
+ 
+void LLWindowSDL::updateIMFocus()
+{
+ 	if (mPreeditor && mWindowIsFocused)
+	{
+		if (!mIMContextIsFocused)
+		{
+			gtk_im_context_focus_in(mIMContext);
+			mIMContextIsFocused = TRUE;
+			updateLanguageTextInputArea();
+		}
+	}
+ 	else
+	{
+		if (mIMContextIsFocused)
+			{
+				gtk_im_context_focus_out(mIMContext);
+				mIMContextIsFocused = FALSE;
+			}
+	}
+}
