diff options
Diffstat (limited to 'indra/llwindow/llwindowsdl.cpp')
-rw-r--r-- | indra/llwindow/llwindowsdl.cpp | 1940 |
1 files changed, 1087 insertions, 853 deletions
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 7433ad6bd2..8c90f917b8 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -25,8 +25,6 @@ * $/LicenseInfo$ */ -#if LL_SDL - #include "linden_common.h" #include "llwindowsdl.h" @@ -40,23 +38,23 @@ #include "lldir.h" #include "llfindlocale.h" -#if LL_GTK -extern "C" { -# include "gtk/gtk.h" -} -#include <locale.h> -#endif // LL_GTK +#ifdef LL_GLIB +#include <glib.h> +#endif extern "C" { # include "fontconfig/fontconfig.h" } -#if LL_LINUX +#if LL_LINUX || __FreeBSD__ // 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> +# define GLX_GLXEXT_PROTOTYPES 1 +# include <GL/glx.h> +# include <stdio.h> #endif // LL_LINUX extern bool gDebugWindowProc; @@ -66,6 +64,43 @@ const S32 MAX_NUM_RESOLUTIONS = 200; // static variable for ATI mouse cursor crash work-around: static bool ATIbug = false; +#if LL_DARWIN + +#include <OpenGL/OpenGL.h> +#include <CoreGraphics/CGDirectDisplay.h> +#include <CoreServices/CoreServices.h> + +bool gHiDPISupport = true; + +namespace +{ + struct NativeKeyEventData { + enum EventType { + KEYUNKNOWN, + KEYUP, + KEYDOWN, + KEYCHAR + }; + + EventType mKeyEvent = KEYUNKNOWN; + uint32_t mEventType = 0; + uint32_t mEventModifiers = 0; + uint32_t mEventKeyCode = 0; + uint32_t mEventChars = 0; + uint32_t mEventUnmodChars = 0; + bool mEventRepeat = false; + } *mRawKeyEvent = NULL; +} +// +// LLWindowMacOSX +// + +bool LLWindowSDL::sUseMultGL = false; + +#endif + +bool hasHIDPI = 0; + // // LLWindowSDL // @@ -97,108 +132,291 @@ void maybe_unlock_display(void) } -#if LL_GTK -// Lazily initialize and check the runtime GTK version for goodness. +#if LL_X11 // static -bool LLWindowSDL::ll_try_gtk_init(void) +Window LLWindowSDL::get_SDL_XWindowID(void) +{ + if (gWindowImplementation) { + return gWindowImplementation->mSDL_XWindowID; + } + return None; +} + +//static +Display* LLWindowSDL::get_SDL_Display(void) +{ + if (gWindowImplementation) { + return gWindowImplementation->mSDL_Display; + } + return NULL; +} +#endif // LL_X11 + +#if LL_X11 + +// Clipboard handing via native X11, base on the implementation in Cool VL by Henri Beauchamp + +namespace { - static bool done_gtk_diag = false; - static bool gtk_is_good = false; - static bool done_setlocale = false; - static bool tried_gtk_init = false; + std::array<Atom, 3> gSupportedAtoms; - if (!done_setlocale) + Atom XA_CLIPBOARD; + Atom XA_TARGETS; + Atom PVT_PASTE_BUFFER; + long const MAX_PASTE_BUFFER_SIZE = 16383; + + void filterSelectionRequest( XEvent aEvent ) { - LL_INFOS() << "Starting GTK Initialization." << LL_ENDL; - maybe_lock_display(); - gtk_disable_setlocale(); - maybe_unlock_display(); - done_setlocale = true; + auto *display = LLWindowSDL::getSDLDisplay(); + auto &request = aEvent.xselectionrequest; + + XSelectionEvent reply { SelectionNotify, aEvent.xany.serial, aEvent.xany.send_event, display, + request.requestor, request.selection, request.target, + request.property,request.time }; + + if (request.target == XA_TARGETS) + { + XChangeProperty(display, request.requestor, request.property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) &gSupportedAtoms.front(), gSupportedAtoms.size()); + } + else if (std::find(gSupportedAtoms.begin(), gSupportedAtoms.end(), request.target) != + gSupportedAtoms.end()) + { + std::string utf8; + if (request.selection == XA_PRIMARY) + utf8 = wstring_to_utf8str(gWindowImplementation->getPrimaryText()); + else + utf8 = wstring_to_utf8str(gWindowImplementation->getSecondaryText()); + + XChangeProperty(display, request.requestor, request.property, + request.target, 8, PropModeReplace, + (unsigned char *) utf8.c_str(), utf8.length()); + } + else if (request.selection == XA_CLIPBOARD) + { + // Did not have what they wanted, so no property set + reply.property = None; + } + else + return; + + XSendEvent(request.display, request.requestor, False, NoEventMask, (XEvent *) &reply); + XSync(display, False); } - if (!tried_gtk_init) + void filterSelectionClearRequest( XEvent aEvent ) { - tried_gtk_init = true; - if (!g_thread_supported ()) g_thread_init (NULL); - maybe_lock_display(); - gtk_is_good = gtk_init_check(NULL, NULL); - maybe_unlock_display(); - if (!gtk_is_good) - LL_WARNS() << "GTK Initialization failed." << LL_ENDL; + auto &request = aEvent.xselectionrequest; + if (request.selection == XA_PRIMARY) + gWindowImplementation->clearPrimaryText(); + else if (request.selection == XA_CLIPBOARD) + gWindowImplementation->clearSecondaryText(); } - if (gtk_is_good && !done_gtk_diag) + int x11_clipboard_filter(void*, SDL_Event *evt) { - LL_INFOS() << "GTK Initialized." << LL_ENDL; - LL_INFOS() << "- Compiled against GTK version " - << GTK_MAJOR_VERSION << "." - << GTK_MINOR_VERSION << "." - << GTK_MICRO_VERSION << LL_ENDL; - LL_INFOS() << "- Running against GTK version " - << gtk_major_version << "." - << gtk_minor_version << "." - << gtk_micro_version << LL_ENDL; + Display *display = LLWindowSDL::getSDLDisplay(); + if (!display) + return 1; + + if (evt->type != SDL_SYSWMEVENT) + return 1; + + auto xevent = evt->syswm.msg->msg.x11.event; + + if (xevent.type == SelectionRequest) + filterSelectionRequest( xevent ); + else if (xevent.type == SelectionClear) + filterSelectionClearRequest( xevent ); + return 1; + } + + bool grab_property(Display* display, Window window, Atom selection, Atom target) + { + if( !display ) + return false; + maybe_lock_display(); - const gchar* gtk_warning = gtk_check_version( - GTK_MAJOR_VERSION, - GTK_MINOR_VERSION, - GTK_MICRO_VERSION); + + XDeleteProperty(display, window, PVT_PASTE_BUFFER); + XFlush(display); + + XConvertSelection(display, selection, target, PVT_PASTE_BUFFER, window, CurrentTime); + + // Unlock the connection so that the SDL event loop may function maybe_unlock_display(); - if (gtk_warning) + + const auto start{ SDL_GetTicks() }; + const auto end{ start + 1000 }; + + XEvent xevent {}; + bool response = false; + + do { - LL_WARNS() << "- GTK COMPATIBILITY WARNING: " << - gtk_warning << LL_ENDL; - gtk_is_good = false; - } else { - LL_INFOS() << "- GTK version is good." << LL_ENDL; - } + SDL_Event event {}; + + // Wait for an event + SDL_WaitEvent(&event); + + // If the event is a window manager event + if (event.type == SDL_SYSWMEVENT) + { + xevent = event.syswm.msg->msg.x11.event; + + if (xevent.type == SelectionNotify && xevent.xselection.requestor == window) + response = true; + } + } while (!response && SDL_GetTicks() < end ); + + return response && xevent.xselection.property != None; + } +} + +void LLWindowSDL::initialiseX11Clipboard() +{ + if (!mSDL_Display) + return; + + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + SDL_SetEventFilter(x11_clipboard_filter, nullptr); + + maybe_lock_display(); + + XA_CLIPBOARD = XInternAtom(mSDL_Display, "CLIPBOARD", False); + + gSupportedAtoms[0] = XInternAtom(mSDL_Display, "UTF8_STRING", False); + gSupportedAtoms[1] = XInternAtom(mSDL_Display, "COMPOUND_TEXT", False); + gSupportedAtoms[2] = XA_STRING; + + // TARGETS atom + XA_TARGETS = XInternAtom(mSDL_Display, "TARGETS", False); + + // SL_PASTE_BUFFER atom + PVT_PASTE_BUFFER = XInternAtom(mSDL_Display, "FS_PASTE_BUFFER", False); + + maybe_unlock_display(); +} + +bool LLWindowSDL::getSelectionText( Atom aSelection, Atom aType, LLWString &text ) +{ + if( !mSDL_Display ) + return false; + + if( !grab_property(mSDL_Display, mSDL_XWindowID, aSelection,aType ) ) + return false; - done_gtk_diag = true; + maybe_lock_display(); + + Atom type; + int format{}; + unsigned long len{},remaining {}; + unsigned char* data = nullptr; + int res = XGetWindowProperty(mSDL_Display, mSDL_XWindowID, + PVT_PASTE_BUFFER, 0, MAX_PASTE_BUFFER_SIZE, False, + AnyPropertyType, &type, &format, &len, + &remaining, &data); + if (data && len) + { + text = LLWString( + utf8str_to_wstring(reinterpret_cast< char const *>( data ) ) + ); + XFree(data); } - return gtk_is_good; + maybe_unlock_display(); + return res == Success; } -#endif // LL_GTK +bool LLWindowSDL::getSelectionText(Atom selection, LLWString& text) +{ + if (!mSDL_Display) + return false; -#if LL_X11 -// static -Window LLWindowSDL::get_SDL_XWindowID(void) + maybe_lock_display(); + + Window owner = XGetSelectionOwner(mSDL_Display, selection); + if (owner == None) + { + if (selection == XA_PRIMARY) + { + owner = DefaultRootWindow(mSDL_Display); + selection = XA_CUT_BUFFER0; + } + else + { + maybe_unlock_display(); + return false; + } + } + + maybe_unlock_display(); + + for( Atom atom : gSupportedAtoms ) + { + if(getSelectionText(selection, atom, text ) ) + return true; + } + + return false; +} + +bool LLWindowSDL::setSelectionText(Atom selection, const LLWString& text) { - if (gWindowImplementation) { - return gWindowImplementation->mSDL_XWindowID; + maybe_lock_display(); + + if (selection == XA_PRIMARY) + { + std::string utf8 = wstring_to_utf8str(text); + XStoreBytes(mSDL_Display, utf8.c_str(), utf8.length() + 1); + mPrimaryClipboard = text; } - return None; + else + mSecondaryClipboard = text; + + XSetSelectionOwner(mSDL_Display, selection, mSDL_XWindowID, CurrentTime); + + auto owner = XGetSelectionOwner(mSDL_Display, selection); + + maybe_unlock_display(); + + return owner == mSDL_XWindowID; } -//static -Display* LLWindowSDL::get_SDL_Display(void) +Display* LLWindowSDL::getSDLDisplay() { - if (gWindowImplementation) { + if (gWindowImplementation) return gWindowImplementation->mSDL_Display; - } - return NULL; + return nullptr; } -#endif // LL_X11 + +#endif LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, - const std::string& title, S32 x, S32 y, S32 width, - S32 height, U32 flags, - bool fullscreen, bool clearBg, - bool disable_vsync, bool use_gl, - bool ignore_pixel_depth, U32 fsaa_samples) - : LLWindow(callbacks, fullscreen, flags), - Lock_Display(NULL), - Unlock_Display(NULL), mGamma(1.0f) + const std::string& title, S32 x, S32 y, S32 width, + S32 height, U32 flags, + bool fullscreen, bool clearBg, + bool disable_vsync, bool use_gl, + bool ignore_pixel_depth, U32 fsaa_samples) + : LLWindow(callbacks, fullscreen, flags), + Lock_Display(NULL), + //Unlock_Display(NULL), mGamma(1.0f) + Unlock_Display(NULL), mGamma(1.0f) { // Initialize the keyboard gKeyboard = new LLKeyboardSDL(); gKeyboard->setCallbacks(callbacks); // Note that we can't set up key-repeat until after SDL has init'd video +#if LL_DARWIN + hasHIDPI = gHiDPISupport; +#endif + // Ignore use_gl for now, only used for drones on PC mWindow = NULL; + mContext = {}; mNeedsResize = false; mOverrideAspectRatio = 0.f; mGrabbyKeyFlags = 0; @@ -212,13 +430,6 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, mSDL_Display = NULL; #endif // LL_X11 -#if LL_GTK - // We MUST be the first to initialize GTK 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 - // Assume 4:3 aspect ratio until we know better mOriginalAspectRatio = 1024.0 / 768.0; @@ -244,9 +455,9 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, #if LL_X11 mFlashing = false; + initialiseX11Clipboard(); #endif // LL_X11 - mKeyScanCode = 0; mKeyVirtualKey = 0; mKeyModifiers = KMOD_NONE; } @@ -258,10 +469,10 @@ static SDL_Surface *Load_BMP_Resource(const char *basename) // 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); + 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); @@ -348,7 +559,7 @@ static int x11_detect_VRAM_kb() if (fp) { LL_INFOS() << "Looking in " << fname - << " for VRAM info..." << LL_ENDL; + << " for VRAM info..." << LL_ENDL; rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: "); fclose(fp); if (0 == rtn) @@ -373,7 +584,7 @@ static int x11_detect_VRAM_kb() else { LL_INFOS() << "Could not open " << fname - << " - skipped." << LL_ENDL; + << " - skipped." << LL_ENDL; // Try old XFree86 log otherwise fname = x_log_location; fname += "XFree86."; @@ -383,7 +594,7 @@ static int x11_detect_VRAM_kb() if (fp) { LL_INFOS() << "Looking in " << fname - << " for VRAM info..." << LL_ENDL; + << " for VRAM info..." << LL_ENDL; rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: "); fclose(fp); if (0 == rtn) @@ -399,95 +610,170 @@ static int x11_detect_VRAM_kb() else { LL_INFOS() << "Could not open " << fname - << " - skipped." << LL_ENDL; + << " - skipped." << LL_ENDL; } } return rtn; } #endif // LL_X11 +void LLWindowSDL::setTitle(const std::string title) +{ + SDL_SetWindowTitle( mWindow, title.c_str() ); +} + +void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) +{ + LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL; + + // If the requested width or height is 0, find the best default for the monitor. + if((width == 0) || (height == 0)) + { + // Scan through the list of modes, looking for one which has: + // height between 700 and 800 + // aspect ratio closest to the user's original mode + S32 resolutionCount = 0; + LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount); + + if(resolutionList != NULL) + { + F32 closestAspect = 0; + U32 closestHeight = 0; + U32 closestWidth = 0; + int i; + + LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL; + + for(i=0; i < resolutionCount; i++) + { + F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight; + + LL_INFOS() << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL; + + if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) && + (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio))) + { + LL_INFOS() << " (new closest mode) " << LL_ENDL; + + // This is the closest mode we've seen yet. + closestWidth = resolutionList[i].mWidth; + closestHeight = resolutionList[i].mHeight; + closestAspect = aspect; + } + } + + width = closestWidth; + height = closestHeight; + } + } + + if((width == 0) || (height == 0)) + { + // Mode search failed for some reason. Use the old-school default. + width = 1024; + height = 768; + } +} + bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool disable_vsync) { //bool glneedsinit = false; LL_INFOS() << "createContext, fullscreen=" << fullscreen << - " size=" << width << "x" << height << LL_ENDL; + " size=" << width << "x" << height << LL_ENDL; // captures don't survive contexts mGrabbyKeyFlags = 0; mReallyCapturedCount = 0; - if (SDL_Init(SDL_INIT_VIDEO) < 0) + std::initializer_list<std::tuple< char const*, char const * > > hintList = + { + {SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"}, + {SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"}, + {SDL_HINT_IME_INTERNAL_EDITING,"1"} + }; + + for( auto hint: hintList ) { - LL_INFOS() << "sdl_init() failed! " << SDL_GetError() << LL_ENDL; - setupFailure("sdl_init() failure, window creation error", "error", OSMB_OK); - return false; + SDL_SetHint( std::get<0>(hint), std::get<1>(hint)); + } + + std::initializer_list<std::tuple<uint32_t, char const*, bool>> initList= + { {SDL_INIT_VIDEO,"SDL_INIT_VIDEO", true}, + {SDL_INIT_AUDIO,"SDL_INIT_AUDIO", false}, + {SDL_INIT_GAMECONTROLLER,"SDL_INIT_GAMECONTROLLER", false}, + {SDL_INIT_SENSOR,"SDL_INIT_SENSOR", false} + }; + + for( auto subSystem : initList) + { + if( SDL_InitSubSystem( std::get<0>(subSystem) ) < 0 ) + { + LL_WARNS() << "SDL_InitSubSystem for " << std::get<1>(subSystem) << " failed " << SDL_GetError() << LL_ENDL; + + if( std::get<2>(subSystem)) + setupFailure("SDL_Init() failure", "error", OSMB_OK); + + } } SDL_version c_sdl_version; SDL_VERSION(&c_sdl_version); LL_INFOS() << "Compiled against SDL " - << int(c_sdl_version.major) << "." - << int(c_sdl_version.minor) << "." - << int(c_sdl_version.patch) << LL_ENDL; - const SDL_version *r_sdl_version; - r_sdl_version = SDL_Linked_Version(); + << int(c_sdl_version.major) << "." + << int(c_sdl_version.minor) << "." + << int(c_sdl_version.patch) << LL_ENDL; + SDL_version r_sdl_version; + SDL_GetVersion(&r_sdl_version); LL_INFOS() << " Running against SDL " - << int(r_sdl_version->major) << "." - << int(r_sdl_version->minor) << "." - << int(r_sdl_version->patch) << LL_ENDL; + << int(r_sdl_version.major) << "." + << int(r_sdl_version.minor) << "." + << int(r_sdl_version.patch) << LL_ENDL; - const SDL_VideoInfo *video_info = SDL_GetVideoInfo( ); - if (!video_info) - { - LL_INFOS() << "SDL_GetVideoInfo() failed! " << SDL_GetError() << LL_ENDL; - setupFailure("SDL_GetVideoInfo() failed, Window creation error", "Error", OSMB_OK); - return false; - } + if (width == 0) + width = 1024; + if (height == 0) + width = 768; - if (video_info->current_h > 0) - { - mOriginalAspectRatio = (float)video_info->current_w / (float)video_info->current_h; - LL_INFOS() << "Original aspect ratio was " << video_info->current_w << ":" << video_info->current_h << "=" << mOriginalAspectRatio << LL_ENDL; - } + mFullscreen = fullscreen; - SDL_EnableUNICODE(1); - SDL_WM_SetCaption(mWindowTitle.c_str(), mWindowTitle.c_str()); + int sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; - // Set the application icon. - SDL_Surface *bmpsurface; - bmpsurface = Load_BMP_Resource("ll_icon.BMP"); - if (bmpsurface) + if( mFullscreen ) { - // 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; + sdlflags |= SDL_WINDOW_FULLSCREEN; + tryFindFullscreenSize( width, height ); } - // note: these SetAttributes make Tom's 9600-on-AMD64 fail to - // get a visual, but it's broken anyway when it does, and without - // these SetAttributes we might easily get an avoidable substandard - // visual to work with on most other machines. - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24); - // We need stencil support for a few (minor) things. - if (!getenv("LL_GL_NO_STENCIL")) - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8); + if(hasHIDPI) sdlflags = sdlflags | SDL_WINDOW_ALLOW_HIGHDPI; - // *FIX: try to toggle vsync here? + mSDLFlags = sdlflags; - mFullscreen = fullscreen; + GLint redBits{8}, greenBits{8}, blueBits{8}, alphaBits{8}; + + GLint depthBits{(bits <= 16) ? 16 : 24}, stencilBits{8}; + + if (getenv("LL_GL_NO_STENCIL")) + stencilBits = 0; - int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT; + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, alphaBits); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, redBits); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, greenBits); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, blueBits); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthBits ); + + // We need stencil support for a few (minor) things. + if (stencilBits) + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits); + // *FIX: try to toggle vsync here? + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); +#if LL_DARWIN + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); +#endif + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -497,80 +783,41 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, mFSAASamples); } - mSDLFlags = sdlflags; + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + mWindow = SDL_CreateWindow( mWindowTitle.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, mSDLFlags ); - if (mFullscreen) + if( mWindow ) { - LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL; + mContext = SDL_GL_CreateContext( mWindow ); - // If the requested width or height is 0, find the best default for the monitor. - if((width == 0) || (height == 0)) + if( mContext == 0 ) { - // Scan through the list of modes, looking for one which has: - // height between 700 and 800 - // aspect ratio closest to the user's original mode - S32 resolutionCount = 0; - LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount); - - if(resolutionList != NULL) - { - F32 closestAspect = 0; - U32 closestHeight = 0; - U32 closestWidth = 0; - int i; - - LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL; - - for(i=0; i < resolutionCount; i++) - { - F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight; - - LL_INFOS() << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL; - - if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) && - (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio))) - { - LL_INFOS() << " (new closest mode) " << LL_ENDL; - - // This is the closest mode we've seen yet. - closestWidth = resolutionList[i].mWidth; - closestHeight = resolutionList[i].mHeight; - closestAspect = aspect; - } - } - - width = closestWidth; - height = closestHeight; - } + LL_WARNS() << "Cannot create GL context " << SDL_GetError() << LL_ENDL; + setupFailure("GL Context creation error creation error", "Error", OSMB_OK); + return false; } + // SDL_GL_SetSwapInterval(1); + } - 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( mFullscreen ) + { if (mWindow) { mFullscreen = true; - mFullscreenWidth = mWindow->w; - mFullscreenHeight = mWindow->h; - mFullscreenBits = mWindow->format->BitsPerPixel; + /* + mFullscreenWidth = mSurface->w; + mFullscreenHeight = mSurface->h; + mFullscreenBits = mSurface->format->BitsPerPixel; + */ + SDL_GetWindowSize(mWindow, &mFullscreenWidth, &mFullscreenHeight); mFullscreenRefresh = -1; LL_INFOS() << "Running at " << mFullscreenWidth - << "x" << mFullscreenHeight - << "x" << mFullscreenBits - << " @ " << mFullscreenRefresh - << LL_ENDL; + << "x" << mFullscreenHeight + << "x" << mFullscreenBits + << " @ " << mFullscreenRefresh + << LL_ENDL; } else { @@ -584,33 +831,27 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b std::string error = llformat("Unable to run fullscreen at %d x %d.\nRunning in window.", width, height); OSMessageBox(error, "Error", OSMB_OK); + return false; } } - - if(!mFullscreen && (mWindow == NULL)) + else { - if (width == 0) - width = 1024; - if (height == 0) - width = 768; - - LL_INFOS() << "createContext: creating window " << width << "x" << height << "x" << bits << LL_ENDL; - 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) { LL_WARNS() << "createContext: window creation failure. SDL: " << SDL_GetError() << LL_ENDL; setupFailure("Window creation error", "Error", OSMB_OK); return false; } - } else if (!mFullscreen && (mWindow != NULL)) + } + + // Set the application icon. + SDL_Surface *bmpsurface; + bmpsurface = Load_BMP_Resource("ll_icon.BMP"); + if (bmpsurface) { - LL_INFOS() << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << LL_ENDL; + SDL_SetWindowIcon(mWindow, bmpsurface); + SDL_FreeSurface(bmpsurface); + bmpsurface = NULL; } // Detect video memory size. @@ -620,39 +861,67 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b { LL_INFOS() << "X11 log-parser detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL; } else + { + PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC queryInteger; + queryInteger = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) + glXGetProcAddressARB((const GLubyte *)"glXQueryCurrentRendererIntegerMESA"); + unsigned int vram_megabytes = 0; + queryInteger(GLX_RENDERER_VIDEO_MEMORY_MESA, &vram_megabytes); + if (!vram_megabytes) { + glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, (int *)&vram_megabytes); + vram_megabytes /= 1024; + } + if (!vram_megabytes) { + glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, (int *)&vram_megabytes); + vram_megabytes /= 1024; + } + gGLManager.mVRAM = vram_megabytes; + } +#elif LL_DARWIN + CGLRendererInfoObj info = 0; + GLint vram_megabytes = 0; + int num_renderers = 0; + auto err = CGLQueryRendererInfo(CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), + &info, &num_renderers); + if (!err) { + CGLDescribeRenderer(info, 0, kCGLRPVideoMemoryMegabytes, &vram_megabytes); + CGLDestroyRendererInfo(info); + } else + vram_megabytes = 256; + gGLManager.mVRAM = vram_megabytes; # 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 = video_info->video_mem / 1024; + gGLManager.mVRAM = 0; if (gGLManager.mVRAM != 0) { LL_INFOS() << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL; } } +*/ // 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); + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &redBits); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &greenBits); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blueBits); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alphaBits); + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthBits); + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencilBits); LL_INFOS() << "GL buffer:" << LL_ENDL; - LL_INFOS() << " Red Bits " << S32(redBits) << LL_ENDL; - LL_INFOS() << " Green Bits " << S32(greenBits) << LL_ENDL; - LL_INFOS() << " Blue Bits " << S32(blueBits) << LL_ENDL; - LL_INFOS() << " Alpha Bits " << S32(alphaBits) << LL_ENDL; - LL_INFOS() << " Depth Bits " << S32(depthBits) << LL_ENDL; - LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL; + LL_INFOS() << " Red Bits " << S32(redBits) << LL_ENDL; + LL_INFOS() << " Green Bits " << S32(greenBits) << LL_ENDL; + LL_INFOS() << " Blue Bits " << S32(blueBits) << LL_ENDL; + LL_INFOS() << " Alpha Bits " << S32(alphaBits) << LL_ENDL; + LL_INFOS() << " Depth Bits " << S32(depthBits) << LL_ENDL; + LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL; GLint colorBits = redBits + greenBits + blueBits + alphaBits; // fixme: actually, it's REALLY important for picking that we get at @@ -660,70 +929,51 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b // relaxed about if we have to. if (colorBits < 32) { +#if 0 close(); setupFailure( - "Second Life requires True Color (32-bit) to run in a window.\n" - "Please go to Control Panels -> Display -> Settings and\n" - "set the screen to 32-bit color.\n" - "Alternately, if you choose to run fullscreen, Second Life\n" - "will automatically adjust the screen each time it runs.", - "Error", - OSMB_OK); - return false; - } - -#if 0 // *FIX: we're going to brave it for now... - if (alphaBits < 8) - { - close(); - setupFailure( - "Second Life is unable to run because it can't get an 8 bit alpha\n" - "channel. Usually this is due to video card driver issues.\n" - "Please make sure you have the latest video card drivers installed.\n" - "Also be sure your monitor is set to True Color (32-bit) in\n" - "Control Panels -> Display -> Settings.\n" - "If you continue to receive this message, contact customer service.", - "Error", - OSMB_OK); + "Second Life requires True Color (32-bit) to run in a window.\n" + "Please go to Control Panels -> Display -> Settings and\n" + "set the screen to 32-bit color.\n" + "Alternately, if you choose to run fullscreen, Second Life\n" + "will automatically adjust the screen each time it runs.", + "Error", + OSMB_OK); return false; - } #endif + } #if LL_X11 /* Grab the window manager specific information */ SDL_SysWMinfo info; SDL_VERSION(&info.version); - if ( SDL_GetWMInfo(&info) ) + if ( SDL_GetWindowWMInfo(mWindow, &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; + mSDL_XWindowID = info.info.x11.window; } else { LL_WARNS() << "We're not running under X11? Wild." - << LL_ENDL; + << LL_ENDL; } } else { LL_WARNS() << "We're not running under any known WM. Wild." - << LL_ENDL; + << LL_ENDL; } #endif // LL_X11 + SDL_StartTextInput(); //make sure multisampling is disabled by default +#if GL_VERSION_1_3 glDisable(GL_MULTISAMPLE_ARB); - - // We need to do this here, once video is init'd - if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, - SDL_DEFAULT_REPEAT_INTERVAL)) - LL_WARNS() << "Couldn't enable key-repeat: " << SDL_GetError() <<LL_ENDL; +#endif // Don't need to get the current gamma, since there's a call that restores it to the system defaults. return true; @@ -761,6 +1011,7 @@ void LLWindowSDL::destroyContext() { LL_INFOS() << "destroyContext begins" << LL_ENDL; + SDL_StopTextInput(); #if LL_X11 mSDL_Display = NULL; mSDL_XWindowID = None; @@ -794,23 +1045,39 @@ LLWindowSDL::~LLWindowSDL() void LLWindowSDL::show() { // *FIX: What to do with SDL? + if (mWindow) + { + SDL_ShowWindow(mWindow); + } } void LLWindowSDL::hide() { // *FIX: What to do with SDL? + if (mWindow) + { + SDL_HideWindow(mWindow); + } } //virtual void LLWindowSDL::minimize() { // *FIX: What to do with SDL? + if (mWindow) + { + SDL_MinimizeWindow(mWindow); + } } //virtual void LLWindowSDL::restore() { // *FIX: What to do with SDL? + if (mWindow) + { + SDL_RestoreWindow(mWindow); + } } @@ -844,7 +1111,7 @@ bool LLWindowSDL::getVisible() // Then what is? if (mWindow) { - result = true; + if( SDL_GetWindowFlags(mWindow) & SDL_WINDOW_SHOWN ) result = true; } return(result); @@ -854,10 +1121,12 @@ bool LLWindowSDL::getMinimized() { bool result = false; - if (mWindow && (1 == mIsMinimized)) + if (mWindow/*&& (1 == mIsMinimized)*/) { - result = true; + if( SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MINIMIZED ) result = true; } + + mIsMinimized = result; return(result); } @@ -868,6 +1137,8 @@ bool LLWindowSDL::getMaximized() if (mWindow) { // TODO + if( SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MAXIMIZED ) result = true; + else result = false; } return(result); @@ -876,7 +1147,15 @@ bool LLWindowSDL::getMaximized() bool LLWindowSDL::maximize() { // TODO - return false; + bool result = false; + + if (mWindow) + { + SDL_MaximizeWindow(mWindow); + result = true; + } + + return result; } bool LLWindowSDL::getFullscreen() @@ -896,9 +1175,13 @@ bool LLWindowSDL::getSize(LLCoordScreen *size) { if (mWindow) { - size->mX = mWindow->w; - size->mY = mWindow->h; - return (true); + /* + if(hasHIDPI) + SDL_GL_GetDrawableSize(mWindow, &size->mX, &size->mY); + else + */ + SDL_GetWindowSize(mWindow, &size->mX, &size->mY); + return (true); } return (false); @@ -908,9 +1191,11 @@ bool LLWindowSDL::getSize(LLCoordWindow *size) { if (mWindow) { - size->mX = mWindow->w; - size->mY = mWindow->h; - return (true); + if(hasHIDPI) + SDL_GL_GetDrawableSize(mWindow, &size->mX, &size->mY); + else + SDL_GetWindowSize(mWindow, &size->mX, &size->mY); + return (true); } return (false); @@ -918,49 +1203,50 @@ bool LLWindowSDL::getSize(LLCoordWindow *size) bool LLWindowSDL::setPosition(const LLCoordScreen position) { + bool result = false; + if(mWindow) { // *FIX: (?) //MacMoveWindow(mWindow, position.mX, position.mY, false); + SDL_SetWindowPosition(mWindow, position.mX, position.mY); + result = true; } - return true; + return result; } -bool LLWindowSDL::setSizeImpl(const LLCoordScreen size) +template< typename T > bool setSizeImpl( const T& newSize, SDL_Window *pWin ) { - if(mWindow) - { - // Push a resize event onto SDL's queue - we'll handle it - // when it comes out again. - SDL_Event event; - event.type = SDL_VIDEORESIZE; - event.resize.w = size.mX; - event.resize.h = size.mY; - SDL_PushEvent(&event); // copied into queue + if( !pWin ) + return false; - return true; - } + auto nFlags = SDL_GetWindowFlags( pWin ); - return false; + if( nFlags & SDL_WINDOW_MAXIMIZED ) + SDL_RestoreWindow( pWin ); + + + SDL_SetWindowSize( pWin, newSize.mX, newSize.mY ); + SDL_Event event; + event.type = SDL_WINDOWEVENT; + event.window.event = SDL_WINDOWEVENT_RESIZED; + event.window.windowID = SDL_GetWindowID( pWin ); + event.window.data1 = newSize.mX; + event.window.data2 = newSize.mY; + SDL_PushEvent( &event ); + + return true; } -bool LLWindowSDL::setSizeImpl(const LLCoordWindow size) +bool LLWindowSDL::setSizeImpl(const LLCoordScreen size) { - if(mWindow) - { - // Push a resize event onto SDL's queue - we'll handle it - // when it comes out again. - SDL_Event event; - event.type = SDL_VIDEORESIZE; - event.resize.w = size.mX; - event.resize.h = size.mY; - SDL_PushEvent(&event); // copied into queue - - return true; - } + return ::setSizeImpl( size, mWindow ); +} - return false; +bool LLWindowSDL::setSizeImpl(const LLCoordWindow size) +{ + return ::setSizeImpl( size, mWindow ); } @@ -968,7 +1254,7 @@ void LLWindowSDL::swapBuffers() { if (mWindow) { - SDL_GL_SwapBuffers(); + SDL_GL_SwapWindow( mWindow ); } } @@ -990,7 +1276,10 @@ F32 LLWindowSDL::getGamma() bool LLWindowSDL::restoreGamma() { //CGDisplayRestoreColorSyncSettings(); - SDL_SetGamma(1.0f, 1.0f, 1.0f); + // SDL_SetGamma(1.0f, 1.0f, 1.0f); + Uint16 ramp; + SDL_CalculateGammaRamp(1.0f, &ramp); + SDL_SetWindowGammaRamp(mWindow, &ramp, &ramp, &ramp); return true; } @@ -999,7 +1288,10 @@ bool LLWindowSDL::setGamma(const F32 gamma) mGamma = gamma; if (mGamma == 0) mGamma = 0.1f; mGamma = 1/mGamma; - SDL_SetGamma(mGamma, mGamma, mGamma); + // SDL_SetGamma(mGamma, mGamma, mGamma); + Uint16 ramp; + SDL_CalculateGammaRamp(mGamma, &ramp); + SDL_SetWindowGammaRamp(mWindow, &ramp, &ramp, &ramp); return true; } @@ -1048,7 +1340,14 @@ bool LLWindowSDL::setCursorPosition(const LLCoordWindow position) //LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL; // do the actual forced cursor move. - SDL_WarpMouse(screen_pos.mX, screen_pos.mY); + + if (mFullscreen) + { + SDL_WarpMouseGlobal(screen_pos.mX, screen_pos.mY); + return result; + } + + SDL_WarpMouseInWindow(mWindow, screen_pos.mX, screen_pos.mY); //LL_INFOS() << llformat("llcw %d,%d -> scr %d,%d", position.mX, position.mY, screen_pos.mX, screen_pos.mY) << LL_ENDL; @@ -1070,21 +1369,14 @@ bool LLWindowSDL::getCursorPosition(LLCoordWindow *position) return convertCoords(screen_pos, position); } +F32 LLWindowSDL::getSystemUISize() +{ + if(hasHIDPI) return 2.0f; + else return 1.f; +} F32 LLWindowSDL::getNativeAspectRatio() { -#if 0 - // RN: this hack presumes that the largest supported resolution is monitor-limited - // and that pixels in that mode are square, therefore defining the native aspect ratio - // of the monitor...this seems to work to a close approximation for most CRTs/LCDs - S32 num_resolutions; - LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions); - - - return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight); - //rn: AC -#endif - // MBW -- there are a couple of bad assumptions here. One is that the display list won't include // ridiculous resolutions nobody would ever use. The other is that the list is in order. @@ -1146,7 +1438,7 @@ void LLWindowSDL::beforeDialog() // it only works in X11 if (running_x11 && mWindow) { - SDL_WM_ToggleFullScreen(mWindow); + SDL_SetWindowFullscreen( mWindow, 0 ); } } } @@ -1162,12 +1454,6 @@ void LLWindowSDL::beforeDialog() } #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(); } @@ -1188,7 +1474,7 @@ void LLWindowSDL::afterDialog() // in X11 if (running_x11 && mWindow) { - SDL_WM_ToggleFullScreen(mWindow); + SDL_SetWindowFullscreen( mWindow, 0 ); } } } @@ -1224,143 +1510,87 @@ void LLWindowSDL::x11_set_urgent(bool urgent) void LLWindowSDL::flashIcon(F32 seconds) { + if (getMinimized()) + { #if !LL_X11 - LL_INFOS() << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; + LL_INFOS() << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; #else - LL_INFOS() << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; + LL_INFOS() << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; - F32 remaining_time = mFlashTimer.getRemainingTimeF32(); - if (remaining_time < seconds) - remaining_time = seconds; - mFlashTimer.reset(); - mFlashTimer.setTimerExpirySec(remaining_time); + F32 remaining_time = mFlashTimer.getRemainingTimeF32(); + if (remaining_time < seconds) + remaining_time = seconds; + mFlashTimer.reset(); + mFlashTimer.setTimerExpirySec(remaining_time); - x11_set_urgent(true); - mFlashing = true; + x11_set_urgent(true); + mFlashing = true; #endif // LL_X11 -} - - -#if LL_GTK -bool LLWindowSDL::isClipboardTextAvailable() -{ - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_NONE); - return gtk_clipboard_wait_is_text_available(clipboard) ? - true : false; - } - return false; // failure -} - -bool LLWindowSDL::pasteTextFromClipboard(LLWString &text) -{ - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_NONE); - gchar * const data = gtk_clipboard_wait_for_text(clipboard); - if (data) - { - text = LLWString(utf8str_to_wstring(data)); - g_free(data); - return true; - } - } - return false; // failure -} - -bool LLWindowSDL::copyTextToClipboard(const LLWString &text) -{ - if (ll_try_gtk_init()) - { - const std::string utf8 = wstring_to_utf8str(text); - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_NONE); - gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length()); - return true; } - return false; // failure } - -bool LLWindowSDL::isPrimaryTextAvailable() -{ - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_SELECTION_PRIMARY); - return gtk_clipboard_wait_is_text_available(clipboard) ? - true : false; - } - return false; // failure -} - -bool LLWindowSDL::pasteTextFromPrimary(LLWString &text) +bool LLWindowSDL::isClipboardTextAvailable() { - if (ll_try_gtk_init()) - { - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_SELECTION_PRIMARY); - gchar * const data = gtk_clipboard_wait_for_text(clipboard); - if (data) - { - text = LLWString(utf8str_to_wstring(data)); - g_free(data); - return true; - } - } - return false; // failure +#if LL_X11 + return mSDL_Display && XGetSelectionOwner(mSDL_Display, XA_CLIPBOARD) != None; +#else + return SDL_HasClipboardText(); +#endif } -bool LLWindowSDL::copyTextToPrimary(const LLWString &text) +bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst) { - if (ll_try_gtk_init()) +#if LL_X11 + return getSelectionText(XA_CLIPBOARD, dst); +#else + auto data = SDL_GetClipboardText(); + if (data) { - const std::string utf8 = wstring_to_utf8str(text); - GtkClipboard * const clipboard = - gtk_clipboard_get(GDK_SELECTION_PRIMARY); - gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length()); + dst = LLWString(utf8str_to_wstring(data)); + SDL_free(data); return true; } - return false; // failure -} - -#else - -bool LLWindowSDL::isClipboardTextAvailable() -{ - return false; // unsupported -} - -bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst) -{ - return false; // unsupported + return false; +#endif } bool LLWindowSDL::copyTextToClipboard(const LLWString &s) { - return false; // unsupported +#if LL_X11 + return setSelectionText(XA_CLIPBOARD, s); +#else + return !SDL_SetClipboardText(wstring_to_utf8str(s).c_str()); +#endif } bool LLWindowSDL::isPrimaryTextAvailable() { +#if LL_X11 + LLWString text; + return getSelectionText(XA_PRIMARY, text) && !text.empty(); +#else return false; // unsupported +#endif } bool LLWindowSDL::pasteTextFromPrimary(LLWString &dst) { +#if LL_X11 + return getSelectionText(XA_PRIMARY, dst); +#else return false; // unsupported +#endif } bool LLWindowSDL::copyTextToPrimary(const LLWString &s) { - return false; // unsupported +#if LL_X11 + return setSelectionText(XA_PRIMARY, s); +#else + return false; // unsupported +#endif } -#endif // LL_GTK - LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions) { if (!mSupportedResolutions) @@ -1368,33 +1598,30 @@ LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_reso 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)) ) + // <FS:ND> Use display no from mWindow/mSurface here? + int max = SDL_GetNumDisplayModes(0); + max = llclamp( max, 0, MAX_NUM_RESOLUTIONS ); + + for( int i =0; i < max; ++i ) { - int count = 0; - while (*modes && count<MAX_NUM_RESOLUTIONS) // they're sorted biggest to smallest, so find end... + SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 }; + if (SDL_GetDisplayMode( 0 , i, &mode) != 0) { - modes++; - count++; + continue; } - while (count--) + int w = mode.w; + int h = mode.h; + if ((w >= 800) && (h >= 600)) { - 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)) ) { - // 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++; - } + mSupportedResolutions[mNumSupportedResolutions].mWidth = w; + mSupportedResolutions[mNumSupportedResolutions].mHeight = h; + mNumSupportedResolutions++; } } } @@ -1410,7 +1637,9 @@ bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) return false; to->mX = from.mX; - to->mY = mWindow->h - from.mY - 1; + int h; + SDL_GetWindowSize(mWindow, nullptr, &h); + to->mY = h - from.mY - 1; return true; } @@ -1421,7 +1650,9 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) return false; to->mX = from.mX; - to->mY = mWindow->h - from.mY - 1; + int h; + SDL_GetWindowSize(mWindow, nullptr, &h); + to->mY = h - from.mY - 1; return true; } @@ -1482,13 +1713,13 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) else mReallyCapturedCount = 0; - SDL_GrabMode wantmode, newmode; + bool wantGrab; if (mReallyCapturedCount <= 0) // uncapture { - wantmode = SDL_GRAB_OFF; + wantGrab = false; } else // capture { - wantmode = SDL_GRAB_ON; + wantGrab = true; } if (mReallyCapturedCount < 0) // yuck, imbalance. @@ -1497,9 +1728,11 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) LL_WARNS() << "ReallyCapture count was < 0" << LL_ENDL; } + bool newGrab = wantGrab; + +#if LL_X11 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 @@ -1512,49 +1745,37 @@ bool LLWindowSDL::SDLReallyCaptureInput(bool capture) *keyboard* input from the window manager, which was frustrating users. */ int result; - if (wantmode == SDL_GRAB_ON) + if (wantGrab == true) { - //LL_INFOS() << "X11 POINTER GRABBY" << LL_ENDL; - //newmode = SDL_WM_GrabInput(wantmode); maybe_lock_display(); result = XGrabPointer(mSDL_Display, mSDL_XWindowID, - True, 0, GrabModeAsync, - GrabModeAsync, - None, None, CurrentTime); + True, 0, GrabModeAsync, + GrabModeAsync, + None, None, CurrentTime); maybe_unlock_display(); if (GrabSuccess == result) - newmode = SDL_GRAB_ON; + newGrab = true; else - newmode = SDL_GRAB_OFF; - } else if (wantmode == SDL_GRAB_OFF) + newGrab = false; + } + else { - //LL_INFOS() << "X11 POINTER UNGRABBY" << LL_ENDL; - newmode = SDL_GRAB_OFF; - //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF); + newGrab = false; 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; + } } - +#endif // LL_X11 // return boolean success for whether we ended up in the desired state - return (capture && SDL_GRAB_ON==newmode) || - (!capture && SDL_GRAB_OFF==newmode); + return capture == newGrab; } -U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, bool gain) +U32 LLWindowSDL::SDLCheckGrabbyKeys(U32 keysym, bool gain) { /* part of the fix for SL-13243: Some popular window managers like to totally eat alt-drag for the purposes of moving windows. We @@ -1572,16 +1793,16 @@ U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, bool gain) U32 mask = 0; switch (keysym) { - case SDLK_LALT: - mask = 1U << 0; break; - case SDLK_RALT: - mask = 1U << 1; break; - case SDLK_LCTRL: - mask = 1U << 2; break; - case SDLK_RCTRL: - mask = 1U << 3; break; - default: - break; + case SDLK_LALT: + mask = 1U << 0; break; + case SDLK_RALT: + mask = 1U << 1; break; + case SDLK_LCTRL: + mask = 1U << 2; break; + case SDLK_RCTRL: + mask = 1U << 3; break; + default: + break; } if (gain) @@ -1679,7 +1900,7 @@ void check_vm_bloat() last_rss_size = this_rss_size; last_vm_size = this_vm_size; -finally: + finally: if (NULL != ptr) { free(ptr); @@ -1694,33 +1915,17 @@ finally: // virtual void LLWindowSDL::processMiscNativeEvents() { -#if LL_GTK - // Pump GTK events to avoid starvation for: - // * DBUS servicing - // * Anything else which quietly hooks into the default glib/GTK loop - 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) - static std::string saved_locale; - saved_locale = ll_safe_string(setlocale(LC_ALL, NULL)); - - // Pump until we've nothing left to do or passed 1/15th of a - // second pumping for this frame. - static LLTimer pump_timer; - pump_timer.reset(); - pump_timer.setTimerExpirySec(1.0f / 15.0f); - do { - // Always do at least one non-blocking pump - gtk_main_iteration_do(false); - } while (gtk_events_pending() && - !pump_timer.hasExpired()); - - setlocale(LC_ALL, saved_locale.c_str() ); - } -#endif // LL_GTK +#if LL_GLIB + // Pump until we've nothing left to do or passed 1/15th of a + // second pumping for this frame. + static LLTimer pump_timer; + pump_timer.reset(); + pump_timer.setTimerExpirySec(1.0f / 15.0f); + do + { + g_main_context_iteration(g_main_context_default(), false); + } while( g_main_context_pending(g_main_context_default()) && !pump_timer.hasExpired()); +#endif // hack - doesn't belong here - but this is just for debugging if (getenv("LL_DEBUG_BLOAT")) @@ -1743,43 +1948,89 @@ void LLWindowSDL::gatherInput() { switch (event.type) { + case SDL_MOUSEWHEEL: + if( event.wheel.y != 0 ) + mCallbacks->handleScrollWheel(this, -event.wheel.y); + break; + case SDL_MOUSEMOTION: { LLCoordWindow winCoord(event.button.x, event.button.y); LLCoordGL openGlCoord; convertCoords(winCoord, &openGlCoord); + + openGlCoord.mX = openGlCoord.mX * getSystemUISize(); + openGlCoord.mY = openGlCoord.mY * getSystemUISize(); + + //LL_INFOS() << "SDL_MOUSEMOTION " << event.button.x << " " << event.button.y << " gl " << openGlCoord.mX << " " << openGlCoord.mY << LL_ENDL; + MASK mask = gKeyboard->currentMask(true); mCallbacks->handleMouseMove(this, openGlCoord, mask); break; } + case SDL_TEXTINPUT: + { + auto string = utf8str_to_utf16str( event.text.text ); + mKeyModifiers = gKeyboard->currentMask( false ); + mInputType = "textinput"; + for( auto key: string ) + { + mKeyVirtualKey = key; + + if( (MASK_CONTROL|MASK_ALT)&mKeyModifiers ) + gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers ); + else + handleUnicodeUTF16( key, mKeyModifiers ); + } + break; + } + case SDL_KEYDOWN: - mKeyScanCode = event.key.keysym.scancode; - mKeyVirtualKey = event.key.keysym.unicode; - mKeyModifiers = event.key.keysym.mod; + mKeyVirtualKey = event.key.keysym.sym; + mKeyModifiers = event.key.keysym.mod; + mInputType = "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); + // treat all possible Enter/Return keys the same + if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) + { + mKeyVirtualKey = SDLK_RETURN; + } + + gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers ); + + // <FS:ND> Slightly hacky :| To make the viewer honor enter (eg to accept form input) we've to not only send handleKeyDown but also send a + // invoke handleUnicodeUTF16 in case the user hits return. + // Note that we cannot blindly use handleUnicodeUTF16 for each SDL_KEYDOWN. Doing so will create bogus keyboard input (like % for cursor left). + if( mKeyVirtualKey == SDLK_RETURN ) + { + // fix return key not working when capslock, scrolllock or numlock are enabled + mKeyModifiers &= (~(KMOD_NUM | KMOD_CAPS | KMOD_MODE | KMOD_SCROLL)); + handleUnicodeUTF16( mKeyVirtualKey, mKeyModifiers ); + } + + // 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: - mKeyScanCode = event.key.keysym.scancode; - mKeyVirtualKey = event.key.keysym.unicode; - mKeyModifiers = event.key.keysym.mod; + mKeyVirtualKey = event.key.keysym.sym; + mKeyModifiers = event.key.keysym.mod; + mInputType = "keyup"; - if (SDLCheckGrabbyKeys(event.key.keysym.sym, false) == 0) - SDLReallyCaptureInput(false); // part of the fix for SL-13243 + // treat all possible Enter/Return keys the same + if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) + { + mKeyVirtualKey = SDLK_RETURN; + } - gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod); - break; + if (SDLCheckGrabbyKeys(mKeyVirtualKey, false) == 0) + SDLReallyCaptureInput(false); // part of the fix for SL-13243 + + gKeyboard->handleKeyUp(mKeyVirtualKey,mKeyModifiers); + break; case SDL_MOUSEBUTTONDOWN: { @@ -1787,7 +2038,11 @@ void LLWindowSDL::gatherInput() LLCoordWindow winCoord(event.button.x, event.button.y); LLCoordGL openGlCoord; convertCoords(winCoord, &openGlCoord); - MASK mask = gKeyboard->currentMask(true); + + openGlCoord.mX *= getSystemUISize(); + openGlCoord.mY *= getSystemUISize(); + + MASK mask = gKeyboard->currentMask(true); if (event.button.button == SDL_BUTTON_LEFT) // SDL doesn't manage double clicking... { @@ -1799,7 +2054,7 @@ void LLWindowSDL::gatherInput() if (++leftClick >= 2) { leftClick = 0; - isDoubleClick = true; + isDoubleClick = true; } } lastLeftDown = now; @@ -1830,7 +2085,7 @@ void LLWindowSDL::gatherInput() else if (event.button.button == SDL_BUTTON_RIGHT) // right { - mCallbacks->handleRightMouseDown(this, openGlCoord, mask); + mCallbacks->handleRightMouseDown(this, openGlCoord, mask); } else if (event.button.button == SDL_BUTTON_MIDDLE) // middle @@ -1850,86 +2105,108 @@ void LLWindowSDL::gatherInput() LLCoordWindow winCoord(event.button.x, event.button.y); LLCoordGL openGlCoord; convertCoords(winCoord, &openGlCoord); - MASK mask = gKeyboard->currentMask(true); + + openGlCoord.mX *= getSystemUISize(); + openGlCoord.mY *= getSystemUISize(); + + MASK mask = gKeyboard->currentMask(true); if (event.button.button == SDL_BUTTON_LEFT) // left - mCallbacks->handleMouseUp(this, openGlCoord, mask); + mCallbacks->handleMouseUp(this, openGlCoord, mask); else if (event.button.button == SDL_BUTTON_RIGHT) // right - mCallbacks->handleRightMouseUp(this, openGlCoord, mask); + mCallbacks->handleRightMouseUp(this, openGlCoord, mask); else if (event.button.button == SDL_BUTTON_MIDDLE) // middle - mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask); + mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask); // don't handle mousewheel here... break; } - case SDL_VIDEOEXPOSE: // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing! - mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h); - break; - - case SDL_VIDEORESIZE: // *FIX: handle this? + case SDL_WINDOWEVENT: // *FIX: handle this? { - LL_INFOS() << "Handling a resize event: " << event.resize.w << - "x" << event.resize.h << LL_ENDL; + if( event.window.event == SDL_WINDOWEVENT_RESIZED + /* || event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED*/ ) // <FS:ND> SDL_WINDOWEVENT_SIZE_CHANGED is followed by SDL_WINDOWEVENT_RESIZED, so handling one shall be enough + { + LL_INFOS() << "Handling a resize event: " << event.window.data1 << "x" << event.window.data2 << LL_ENDL; - S32 width = llmax(event.resize.w, (S32)mMinWindowWidth); - S32 height = llmax(event.resize.h, (S32)mMinWindowHeight); + S32 width = llmax(event.window.data1, (S32)mMinWindowWidth); + S32 height = llmax(event.window.data2, (S32)mMinWindowHeight); - // *FIX: I'm not sure this is necessary! - mWindow = SDL_SetVideoMode(width, height, 32, mSDLFlags); - if (!mWindow) - { - // *FIX: More informative dialog? - LL_INFOS() << "Could not recreate context after resize! Quitting..." << LL_ENDL; - if(mCallbacks->handleCloseRequest(this)) + // *FIX: I'm not sure this is necessary! + // <FS:ND> I think is is not + // SDL_SetWindowSize(mWindow, width, height); + // + + mCallbacks->handleResize(this, width * getSystemUISize(), height * getSystemUISize()); + } + else if(event.window.event == SDL_WINDOWEVENT_ENTER) { - // Get the app to initiate cleanup. - mCallbacks->handleQuit(this); - // The app is responsible for calling destroyWindow when done with GL + LL_INFOS() << "SDL_WINDOWEVENT_ENTER" << LL_ENDL; + if(!mHaveInputFocus) mCallbacks->handleFocus(this); + mHaveInputFocus = true; } - break; - } - - mCallbacks->handleResize(this, width, height); - break; - } - case SDL_ACTIVEEVENT: - if (event.active.state & SDL_APPINPUTFOCUS) + else if(event.window.event == SDL_WINDOWEVENT_LEAVE) { - // 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) - { - mHaveInputFocus = !!event.active.gain; + LL_INFOS() << "SDL_WINDOWEVENT_LEAVE" << LL_ENDL; + if(mHaveInputFocus) mCallbacks->handleFocusLost(this); + mHaveInputFocus = false; + } + else if( event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED ) // <FS:ND> What about SDL_WINDOWEVENT_ENTER (mouse focus) + { + // 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]. + mHaveInputFocus = true; - if (mHaveInputFocus) mCallbacks->handleFocus(this); - else + } + else if( event.window.event == SDL_WINDOWEVENT_FOCUS_LOST ) // <FS:ND> What about SDL_WINDOWEVENT_LEAVE (mouse focus) + { + // 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]. + mHaveInputFocus = false; + mCallbacks->handleFocusLost(this); - } } - if (event.active.state & SDL_APPACTIVE) + /* + + Bug : the app remains inactive when maximized .. + + else if(event.window.event == SDL_WINDOWEVENT_MINIMIZED) { - // Change in iconification/minimization state. - if ((!event.active.gain) != mIsMinimized) - { - mIsMinimized = (!event.active.gain); + LL_INFOS() << "SDL_WINDOWEVENT_MINIMIZED" << LL_ENDL; + if(!mIsMinimized) mCallbacks->handleActivate(this,false); + mIsMinimized = true; + } + else if(event.window.event == SDL_WINDOWEVENT_MAXIMIZED) + { + LL_INFOS() << "SDL_WINDOWEVENT_MAXIMIZED" << LL_ENDL; + if(mIsMinimized) mCallbacks->handleActivate(this,true); + mIsMinimized = false; + } + */ + else if (event.window.event == SDL_WINDOWEVENT_EXPOSED) + { + int w, h; + SDL_GL_GetDrawableSize(mWindow, &w, &h); - mCallbacks->handleActivate(this, !mIsMinimized); - LL_INFOS() << "SDL deiconification state switched to " << bool(event.active.gain) << LL_ENDL; - } - else - { - LL_INFOS() << "Ignored bogus redundant SDL deiconification state switch to " << bool(event.active.gain) << LL_ENDL; - } + mCallbacks->handlePaint(this, 0, 0, w, h); } - break; + else if( event.window.event == SDL_WINDOWEVENT_MINIMIZED || + event.window.event == SDL_WINDOWEVENT_MAXIMIZED || + event.window.event == SDL_WINDOWEVENT_RESTORED || + event.window.event == SDL_WINDOWEVENT_EXPOSED || + event.window.event == SDL_WINDOWEVENT_SHOWN ) + { + mIsMinimized = (event.window.event == SDL_WINDOWEVENT_MINIMIZED); + mCallbacks->handleActivate(this, !mIsMinimized); + LL_INFOS() << "SDL deiconification state switched to " << mIsMinimized << LL_ENDL; + } + + break; + } case SDL_QUIT: if(mCallbacks->handleCloseRequest(this)) { @@ -1938,9 +2215,9 @@ void LLWindowSDL::gatherInput() // The app is responsible for calling destroyWindow when done with GL } break; - default: - //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL; - break; + default: + //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL; + break; } } @@ -1968,21 +2245,21 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty { SDL_Surface *cursurface; LL_DEBUGS() << "Loaded cursor file " << filename << " " - << bmpsurface->w << "x" << bmpsurface->h << LL_ENDL; + << bmpsurface->w << "x" << bmpsurface->h << LL_ENDL; cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE, - bmpsurface->w, - bmpsurface->h, - 32, - SDL_SwapLE32(0xFFU), - SDL_SwapLE32(0xFF00U), - SDL_SwapLE32(0xFF0000U), - SDL_SwapLE32(0xFF000000U)); + 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)) + cursurface, NULL)) { // n.b. we already checked that width is a multiple of 8. const int bitmap_bytes = (cursurface->w * cursurface->h) / 8; @@ -1997,26 +2274,26 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty 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*)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); + || (srcgreen != 200) + || (srcblue != 200); bool data_bit = mask_bit && (srcgreen <= 80);//not 0x80 unsigned char bit_offset = (cursurface->w/8) * i - + j/8; + + 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); + (Uint8*)cursor_mask, + cursurface->w, cursurface->h, + hotx, hoty); delete[] cursor_data; delete[] cursor_mask; } else { @@ -2207,8 +2484,6 @@ void LLWindowSDL::hideCursorUntilMouseMove() } } - - // // LLSplashScreenSDL - I don't think we'll bother to implement this; it's // fairly obsolete at this point. @@ -2233,133 +2508,71 @@ void LLSplashScreenSDL::hideImpl() { } - - -#if LL_GTK -static void response_callback (GtkDialog *dialog, - gint arg1, - gpointer user_data) -{ - gint *response = (gint*)user_data; - *response = arg1; - gtk_widget_destroy(GTK_WIDGET(dialog)); - gtk_main_quit(); -} - S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type) { - S32 rtn = OSBTN_CANCEL; - - if(gWindowImplementation != NULL) - gWindowImplementation->beforeDialog(); + SDL_MessageBoxData oData = { SDL_MESSAGEBOX_INFORMATION, nullptr, caption.c_str(), text.c_str(), 0, nullptr, nullptr }; + SDL_MessageBoxButtonData btnOk[] = {{SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, OSBTN_OK, "OK" }}; + SDL_MessageBoxButtonData btnOkCancel [] = {{SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, OSBTN_OK, "OK" }, {SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, OSBTN_CANCEL, "Cancel"} }; + SDL_MessageBoxButtonData btnYesNo[] = { {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, OSBTN_YES, "Yes" }, {SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, OSBTN_NO, "No"} }; - if (LLWindowSDL::ll_try_gtk_init()) + switch (type) { - GtkWidget *win = NULL; - - LL_INFOS() << "Creating a dialog because we're in windowed mode and GTK is happy." << LL_ENDL; - - GtkDialogFlags flags = GTK_DIALOG_MODAL; - GtkMessageType messagetype; - GtkButtonsType buttons; - switch (type) - { default: case OSMB_OK: - messagetype = GTK_MESSAGE_WARNING; - buttons = GTK_BUTTONS_OK; + oData.flags = SDL_MESSAGEBOX_WARNING; + oData.buttons = btnOk; + oData.numbuttons = 1; break; case OSMB_OKCANCEL: - messagetype = GTK_MESSAGE_QUESTION; - buttons = GTK_BUTTONS_OK_CANCEL; + oData.flags = SDL_MESSAGEBOX_INFORMATION; + oData.buttons = btnOkCancel; + oData.numbuttons = 2; break; case OSMB_YESNO: - messagetype = GTK_MESSAGE_QUESTION; - buttons = GTK_BUTTONS_YES_NO; + oData.flags = SDL_MESSAGEBOX_INFORMATION; + oData.buttons = btnYesNo; + oData.numbuttons = 2; break; - } - win = gtk_message_dialog_new(NULL, flags, messagetype, buttons, "%s", - text.c_str()); - -# 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_position(GTK_WINDOW(win), - GTK_WIN_POS_CENTER_ON_PARENT); - - gtk_window_set_type_hint(GTK_WINDOW(win), - GDK_WINDOW_TYPE_HINT_DIALOG); - - if (!caption.empty()) - gtk_window_set_title(GTK_WINDOW(win), caption.c_str()); - - 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(); - - //LL_INFOS() << "response: " << response << LL_ENDL; - 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 - { - LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL; - LL_INFOS() << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << LL_ENDL; - rtn = OSBTN_OK; } - if(gWindowImplementation != NULL) - gWindowImplementation->afterDialog(); + int btn{0}; + if( 0 == SDL_ShowMessageBox( &oData, &btn ) ) + return btn; + return OSBTN_CANCEL; +} - return rtn; +bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) +{ + return (false); } -static void color_changed_callback(GtkWidget *widget, - gpointer user_data) +#if LL_DARWIN +LLSD LLWindowSDL::getNativeKeyData() { - GtkColorSelection *colorsel = GTK_COLOR_SELECTION(widget); - GdkColor *colorp = (GdkColor*)user_data; + LLSD result = LLSD::emptyMap(); - gtk_color_selection_get_current_color(colorsel, colorp); -} + if(mRawKeyEvent) + { + result["event_type"] = LLSD::Integer(mRawKeyEvent->mEventType); + result["event_modifiers"] = LLSD::Integer(mRawKeyEvent->mEventModifiers); + result["event_keycode"] = LLSD::Integer(mRawKeyEvent->mEventKeyCode); + result["event_chars"] = (mRawKeyEvent->mEventChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventChars)) : LLSD(); + result["event_umodchars"] = (mRawKeyEvent->mEventUnmodChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventUnmodChars)) : LLSD(); + result["event_isrepeat"] = LLSD::Boolean(mRawKeyEvent->mEventRepeat); + } + LL_DEBUGS() << "native key data is: " << result << LL_ENDL; + return result; +} +#else /* Make the raw keyboard data available - used to poke through to LLQtWebKit so that Qt/Webkit has access to the virtual keycodes etc. that it needs */ LLSD LLWindowSDL::getNativeKeyData() { - LLSD result = LLSD::emptyMap(); + LLSD result = LLSD::emptyMap(); U32 modifiers = 0; // pretend-native modifiers... oh what a tangled web we weave! @@ -2377,99 +2590,15 @@ LLSD LLWindowSDL::getNativeKeyData() // *todo: test ALTs - I don't have a case for testing these. Do you? // *todo: NUM? - I don't care enough right now (and it's not a GDK modifier). - result["scan_code"] = (S32)mKeyScanCode; - result["virtual_key"] = (S32)mKeyVirtualKey; + result["virtual_key"] = (S32)mKeyVirtualKey; + result["virtual_key_win"] = (S32)LLKeyboardSDL::mapSDL2toWin( mKeyVirtualKey ); result["modifiers"] = (S32)modifiers; - - return result; -} - - -bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) -{ - bool rtn = false; - - beforeDialog(); - - if (ll_try_gtk_init()) - { - 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.pixel = 0; - orig_color.red = guint16(65535 * *r); - orig_color.green= guint16(65535 * *g); - orig_color.blue = guint16(65535 * *b); - color = orig_color; - - gtk_color_selection_set_previous_color (colorsel, &color); - gtk_color_selection_set_current_color (colorsel, &color); - gtk_color_selection_set_has_palette (colorsel, true); - gtk_color_selection_set_has_opacity_control(colorsel, false); - - gint response = GTK_RESPONSE_NONE; - g_signal_connect (win, - "response", - G_CALLBACK (response_callback), - &response); - - g_signal_connect (G_OBJECT (colorsel), "color_changed", - G_CALLBACK (color_changed_callback), - &color); - - gtk_window_set_modal(GTK_WINDOW(win), true); - gtk_widget_show_all(win); - // hide the help button - we don't service it. - gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button); - gtk_main(); - - if (response == GTK_RESPONSE_OK && - (orig_color.red != color.red - || orig_color.green != color.green - || orig_color.blue != color.blue) ) - { - *r = color.red / 65535.0f; - *g = color.green / 65535.0f; - *b = color.blue / 65535.0f; - rtn = true; - } - } - - afterDialog(); - - return rtn; -} -#else -S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type) -{ - LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL; - return 0; -} - -bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) -{ - return (false); + result["input_type"] = mInputType; + return result; } -#endif // LL_GTK +#endif // LL_DARWIN -#if LL_LINUX +#if LL_LINUX || LL_SOLARIS || __FreeBSD__ // extracted from spawnWebBrowser for clarity and to eliminate // compiler confusion regarding close(int fd) vs. LLWindow::close() void exec_cmd(const std::string& cmd, const std::string& arg) @@ -2481,9 +2610,32 @@ void exec_cmd(const std::string& cmd, const std::string& arg) { // 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); + // close(0); + // close(1); + // close(2); + // <FS:TS> Reopen stdin, stdout, and stderr to /dev/null. + // It's good practice to always have those file + // descriptors open to something, lest the exec'd + // program actually try to use them. + FILE *result; + result = freopen("/dev/null","r",stdin); + if (result == NULL) + { + LL_WARNS() << "Error reopening stdin for web browser: " + << strerror(errno) << LL_ENDL; + } + result = freopen("/dev/null","w",stdout); + if (result == NULL) + { + LL_WARNS() << "Error reopening stdout for web browser: " + << strerror(errno) << LL_ENDL; + } + result = freopen("/dev/null","w",stderr); + if (result == NULL) + { + LL_WARNS() << "Error reopening stderr for web browser: " + << strerror(errno) << LL_ENDL; + } // end ourself by running the command execv(cmd.c_str(), argv); /* Flawfinder: ignore */ // if execv returns at all, there was a problem. @@ -2525,7 +2677,7 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) LL_INFOS() << "spawn_web_browser: " << escaped_url << LL_ENDL; -#if LL_LINUX +#if LL_LINUX || __FreeBSD__ # if LL_X11 if (mSDL_Display) { @@ -2537,43 +2689,65 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) # endif // LL_X11 std::string cmd, arg; +#ifdef LL_USESYSTEMLIBS + cmd = gDirUtilp->getExecutableDir(); +#else cmd = gDirUtilp->getAppRODataDir(); cmd += gDirUtilp->getDirDelimiter(); cmd += "etc"; +#endif cmd += gDirUtilp->getDirDelimiter(); cmd += "launch_url.sh"; arg = escaped_url; exec_cmd(cmd, arg); -#endif // LL_LINUX - LL_INFOS() << "spawn_web_browser returning." << LL_ENDL; -} +#elif LL_DARWIN + S32 result = 0; + CFURLRef urlRef = NULL; -void *LLWindowSDL::getPlatformWindow() -{ -#if LL_GTK && LL_LLMOZLIB_ENABLED - if (LLWindowSDL::ll_try_gtk_init()) + LL_INFOS() << "Opening URL " << escaped_url << LL_ENDL; + + CFStringRef stringRef = CFStringCreateWithCString(NULL, escaped_url.c_str(), kCFStringEncodingUTF8); + if (stringRef) { - maybe_lock_display(); + // This will succeed if the string is a full URL, including the http:// + // Note that URLs specified this way need to be properly percent-escaped. + urlRef = CFURLCreateWithString(NULL, stringRef, NULL); - 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); + // Don't use CRURLCreateWithFileSystemPath -- only want valid URLs - maybe_unlock_display(); + CFRelease(stringRef); + } - return rtnw; + if (urlRef) + { + result = LSOpenCFURLRef(urlRef, NULL); + + if (result != noErr) + { + LL_INFOS() << "Error " << result << " on open." << LL_ENDL; + } + + CFRelease(urlRef); } -#endif // LL_GTK && LL_LLMOZLIB_ENABLED - // Unixoid mozilla really needs GTK. + else + { + LL_INFOS() << "Error: couldn't create URL." << LL_ENDL; + } + +#endif // LL_LINUX + + LL_INFOS() << "spawn_web_browser returning." << LL_ENDL; +} + +void LLWindowSDL::openFile(const std::string& file_name) +{ + spawnWebBrowser("file://"+file_name,true); +} + +void *LLWindowSDL::getPlatformWindow() +{ return NULL; } @@ -2634,10 +2808,10 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() LL_INFOS("AppInit") << "Variant " << locale->variant << LL_ENDL; LL_INFOS() << "Preferring fonts of language: " - << locale->lang - << LL_ENDL; + << locale->lang + << LL_ENDL; sort_order = "lang=" + std::string(locale->lang) + ":" - + sort_order; + + sort_order; } } FL_FreeLocale(&locale); @@ -2655,7 +2829,7 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() // Sort the list of system fonts from most-to-least-desirable. FcResult result; fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, - NULL, &result); + NULL, &result); FcPatternDestroy(sortpat); } @@ -2669,8 +2843,8 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() { FcChar8 *filename; if (FcResultMatch == FcPatternGetString(fs->fonts[i], - FC_FILE, 0, - &filename) + FC_FILE, 0, + &filename) && filename) { rtns.push_back(std::string((const char*)filename)); @@ -2694,4 +2868,64 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() return rtns; } -#endif // LL_SDL + +void* LLWindowSDL::createSharedContext() +{ + auto *pContext = SDL_GL_CreateContext(mWindow); + if ( pContext) + { + SDL_GL_SetSwapInterval(0); + SDL_GL_MakeCurrent(mWindow, mContext); + + LLCoordScreen size; + if (getSize(&size)) + setSize(size); + + LL_DEBUGS() << "Creating shared OpenGL context successful!" << LL_ENDL; + + return (void*)pContext; + } + + LL_WARNS() << "Creating shared OpenGL context failed!" << LL_ENDL; + + return nullptr; +} + +void LLWindowSDL::makeContextCurrent(void* contextPtr) +{ + LL_PROFILER_GPU_CONTEXT; + SDL_GL_MakeCurrent( mWindow, contextPtr ); +} + +void LLWindowSDL::destroySharedContext(void* contextPtr) +{ + SDL_GL_DeleteContext( contextPtr ); +} + +void LLWindowSDL::toggleVSync(bool enable_vsync) +{ + if( !enable_vsync) + { + SDL_GL_SetSwapInterval(0); + SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC,"0",SDL_HINT_OVERRIDE); + } + else + { + SDL_GL_SetSwapInterval(1); + SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC,"1",SDL_HINT_OVERRIDE); + } +} + +void LLWindowSDL::setLanguageTextInput(const LLCoordGL& position) +{ + LLCoordWindow win_pos; + convertCoords( position, &win_pos ); + + SDL_Rect r; + r.x = win_pos.mX; + r.y = win_pos.mY; + r.w = 500; + r.h = 16; + + SDL_SetTextInputRect(&r); +} |