123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303 |
- /**
- * @file llwindowsdl.cpp
- * @brief SDL implementation of LLWindow class
- * @author This module has many fathers, and it shows.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #if LL_LINUX
- #include "linden_common.h"
- #include "indra_constants.h"
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include "fontconfig/fontconfig.h"
- #include "llwindowsdl.h"
- #include <regex> // For regex in x11_detect_vram_kb_from_file()
- #include "lldir.h"
- #include "llfasttimer.h"
- #include "llfindlocale.h"
- #include "llgl.h"
- #include "llglslshader.h"
- #include "llkeyboardsdl.h"
- #include "llstring.h"
- #include "lltimer.h"
- // *HACK: 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* sWindowImplementation = NULL;
- // *HACK: to avoid white screen flickering; defined in llviewerdisplay.cpp
- extern U32 gFrameSleepTime;
- bool gXlibThreadSafe = false;
- bool gXWayland = false;
- bool gUseFullDesktop = false;
- constexpr S32 MAX_NUM_RESOLUTIONS = 200;
- S32 exec_cmd(const std::string& cmd, const std::string& arg)
- {
- char* const argv[] = { (char*)cmd.c_str(), (char*)arg.c_str(), NULL };
- fflush(NULL);
- pid_t pid = fork();
- if (pid == 0)
- {
- // Child path. 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);
- // If execv returns at all, there was a problem.
- llwarns << "execv() failure when trying to start: " << cmd << llendl;
- _exit(1); // _exit because we do not want atexit() clean-up !
- }
- else if (pid > 0)
- {
- // Parent path. Wait for child to die.
- int child_exit_status;
- waitpid(pid, &child_exit_status, 0);
- return child_exit_status;
- }
- else
- {
- llwarns << "Fork failure." << llendl;
- }
- return -1;
- }
- //static
- void LLWindowSDL::initXlibThreads()
- {
- // Ensure Xlib is started in thread-safe state, so that the NVIDIA drivers
- // can use multi-threading.
- if (!gXlibThreadSafe)
- {
- gXlibThreadSafe = XInitThreads();
- if (gXlibThreadSafe)
- {
- llinfos << "Xlib successfully initialized in thread-safe state"
- << llendl;
- }
- else
- {
- llwarns << "Failed to initialize Xlib in thread-safe state: NVIDIA drivers will run single-threaded."
- << llendl;
- }
- }
- }
- //static
- Window LLWindowSDL::getSDLXWindowID()
- {
- if (sWindowImplementation)
- {
- return sWindowImplementation->mSDL_XWindowID;
- }
- return None;
- }
- //static
- Display* LLWindowSDL::getSDLDisplay()
- {
- if (sWindowImplementation)
- {
- return sWindowImplementation->mSDL_Display;
- }
- return NULL;
- }
- LLWindowSDL::LLWindowSDL(const std::string& title, S32 x, S32 y, U32 width,
- U32 height, U32 flags, bool fullscreen,
- bool disable_vsync, U32 fsaa_samples)
- : LLWindow(fullscreen, flags),
- mWindow(NULL),
- mSDLFlags(0),
- mInitialPosX(x),
- mInitialPosY(y),
- mPosOffsetX(-1),
- mPosOffsetY(-1),
- mCustomGammaSet(false),
- mKeyVirtualKey(0),
- mKeyModifiers(KMOD_NONE),
- mCaptured(false),
- mGrabbyKeyFlags(0),
- mFSAASamples(fsaa_samples),
- mOriginalAspectRatio(4.f / 3.f), // Assume 4:3 until we know better
- mWindowTitle(title.empty() ? "SL viewer" : title)
- {
- // Initialize the keyboard. Note that we cannot set up key-repeat until
- // after SDL has initialized the video
- gKeyboardp = new LLKeyboardSDL();
- // This should have already been called in LLSplashScreenSDL, but better
- // safe than sorry...
- initXlibThreads();
- // Wayland *SUCKS*, and XWayland is *NOT* 100% X11-compatible...
- char* wayland_env = getenv("WAYLAND_DISPLAY");
- gXWayland = wayland_env && wayland_env[0];
- if (gXWayland)
- {
- llwarns << "XWayland compatibility mode detected. This will cause unexpected behaviours. The viewer is a genuine X11 application, not a Wayland one, please run it under a genuine X11 server. NO SUPPORT provided for viewer sessions ran under XWayland !"
- << llendl;
- }
- mSDL_XWindowID = None;
- mSDL_Display = NULL;
- mContext = {};
- memset(mCurrentGammaRamp, 0, sizeof(mCurrentGammaRamp));
- memset(mPrevGammaRamp, 0, sizeof(mPrevGammaRamp));
- // 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 (!gGLManager.initGL())
- {
- setupFailure("Could not properly initialize OpenGL !");
- }
- // Start with arrow cursor
- initCursors();
- setCursor(UI_CURSOR_ARROW);
- }
- stop_glerror();
- sWindowImplementation = this;
- mFlashing = false;
- initialiseX11Clipboard();
- }
- //virtual
- LLWindowSDL::~LLWindowSDL()
- {
- quitCursors();
- destroyContext();
- if (mSupportedResolutions)
- {
- delete[] mSupportedResolutions;
- mSupportedResolutions = NULL;
- }
- sWindowImplementation = NULL;
- }
- static SDL_Surface* load_bmp_resource(const char* basename)
- {
- constexpr S32 PATH_BUFFER_SIZE = 1000;
- char path_buffer[PATH_BUFFER_SIZE];
- // Figure out where our BMP is living on the disk
- snprintf(path_buffer, PATH_BUFFER_SIZE - 1, "%s/res-sdl/%s",
- gDirUtil.getAppRODataDir().c_str(), basename);
- path_buffer[PATH_BUFFER_SIZE - 1] = '\0';
- return SDL_LoadBMP(path_buffer);
- }
- // This function scans the Xorg log to determine the amount of VRAM available
- // to the system.
- // Returns -1 if it could not open the file.
- // 0 if it could open the file but could not detect the amount of
- // VRAM.
- // >0 (VRAM amount in kilobytes) if successful.
- static S32 x11_detect_vram_kb_from_file(const std::string& filename)
- {
- std::ifstream in(filename.c_str());
- if (!in.is_open())
- {
- LL_DEBUGS("Window") << "Could not open file: " << filename << LL_ENDL;
- return -1;
- }
- std::regex pattern(".*?(VRAM|Memory|Video\\s?RAM)\\D*(\\d+)\\s?([kK]B?)");
- S32 amount = 0;
- std::string line;
- while (std::getline(in, line))
- {
- std::cmatch match;
- if (std::regex_search(line.c_str(), match, pattern))
- {
- amount = atoi(std::string(match[2]).c_str());
- LL_DEBUGS("Window") << "Match found in line: " << line
- << "VRAM amount: " << amount << LL_ENDL;
- }
- }
- in.close();
- return amount;
- }
- static S32 x11_detect_vram_kb()
- {
- S32 amount = 0;
- // Let the user override the detection in case it fails on their system.
- // They can specify the amount of VRAM in megabytes, via the LL_VRAM_MB
- // environment variable.
- char* vram_override = getenv("LL_VRAM_MB");
- if (vram_override)
- {
- amount = atoi(vram_override);
- if (amount > 0)
- {
- llinfos << "Amount of VRAM overridden via the LL_VRAM_MB environment variable; detection step skipped."
- << llendl;
- return 1024 * amount; // Converted to kilobytes
- }
- }
- // We parse VGL_DISPLAY first so we can grab the right Xorg filename if we
- // are using VirtualGL (like Optimus systems do).
- char* display_env = getenv("VGL_DISPLAY"); // e.g. :0 or :0.0 or :1.0 etc
- if (!display_env)
- {
- display_env = getenv("DISPLAY");
- }
- // Parse DISPLAY number so we can go grab the right log file
- S32 display_num = 0;
- if (display_env && display_env[0] == ':' && display_env[1] >= '0' &&
- display_env[1] <= '9')
- {
- display_num = display_env[1] - '0';
- }
- // *TODO: we could be smarter and see which of Xorg/XFree86 has the
- // freshest time-stamp.
- // Try Xorg log first
- std::string x_log_location = "/var/log/";
- std::string fname = x_log_location + "Xorg.";
- fname += ('0' + display_num);
- fname += ".log";
- llinfos << "Looking in " << fname << " for VRAM info..." << llendl;
- amount = x11_detect_vram_kb_from_file(fname);
- if (amount < 0)
- {
- llinfos << "Could not open " << fname << " - skipped." << llendl;
- // Try old XFree86 log otherwise
- fname = x_log_location + "XFree86.";
- fname += ('0' + display_num);
- fname += ".log";
- amount = x11_detect_vram_kb_from_file(fname);
- if (amount < 0)
- {
- llinfos << "Could not open " << fname << " - skipped." << llendl;
- amount = 0;
- }
- }
- if (amount > 0)
- {
- llinfos << "X11 log-parser detected " << amount / 1024 << "MB VRAM."
- << llendl;
- }
- else
- {
- llwarns << "VRAM amount detection failed. You could use the LL_VRAM_MB environment variable to specify it. "
- << llendl;
- }
- return amount;
- }
- //virtual
- void LLWindowSDL::setWindowTitle(const std::string& title)
- {
- // Remember the new title, for when we switch context
- mWindowTitle = title;
- if (mWindow)
- {
- SDL_SetWindowTitle(mWindow, title.c_str());
- }
- }
- //virtual
- bool LLWindowSDL::getFullScreenSize(S32& width, S32& height)
- {
- // When width and height are not 0, consider we already know what size we
- // can use.
- if (width != 0 && height != 0)
- {
- return true;
- }
- // Scan through the list of modes, looking for one which has height between
- // 700 and 800 and aspect ratio closest to the user's original mode.
- S32 res_count = 0;
- LLWindowResolution* res_list = getSupportedResolutions(res_count);
- if (res_list)
- {
- F32 closest_aspect = 0.f;
- S32 closest_width = 0;
- S32 closest_height = 0;
- llinfos << "Searching for a display mode, original aspect is "
- << mOriginalAspectRatio << llendl;
- for (S32 i = 0; i < res_count; ++i)
- {
- S32 h = res_list[i].mHeight;
- S32 w = res_list[i].mWidth;
- F32 aspect = (F32)w / (F32)h;
- llinfos << "width = " << w << " - height = " << h << " - aspect = "
- << aspect;
- if (h >= 700 && h <= 800 &&
- fabs(aspect - mOriginalAspectRatio) <
- fabs(closest_aspect - mOriginalAspectRatio))
- {
- llcont << " (new closest mode)";
- closest_width = w;
- closest_height = h;
- closest_aspect = aspect;
- }
- llcont << llendl;
- }
- width = closest_width;
- height = closest_height;
- }
- if (width == 0 || height == 0)
- {
- // Mode search failed: used some common/acceptable default.
- width = 1024;
- height = 768;
- return false;
- }
- return true;
- }
- // The Cool VL Viewer window icon is opaque, so no need to bother about the
- // transparency code... HB
- #define WINDOW_ICON_TANSPARENCY 0
- // Reimplementation of SDL1's mask generation for SDL_WM_SetIcon()
- static Uint8* generate_icon_mask(SDL_Surface* icon)
- {
- S32 width = icon->w;
- S32 height = icon->h;
- S32 bpl = (width + 7) / 8; // Bytes per line
- S32 mask_len = height * bpl;
- Uint8* mask = (Uint8*)malloc(mask_len);
- if (!mask)
- {
- return NULL;
- }
- // Set as an opaque mask (all bits at 1 in the mask)
- memset((void*)mask, ~0, mask_len);
- #if WINDOW_ICON_TANSPARENCY
- Uint32 colorkey;
- if (SDL_GetColorKey(icon, &colorkey) == 0)
- {
- S32 picth = icon->pitch;
- switch (icon->format->BytesPerPixel)
- {
- case 1:
- {
- for (S32 y = 0; y < height; ++y)
- {
- Uint8* pixels = (Uint8*)icon->pixels + y * picth;
- for (S32 x = 0; x < width; ++x)
- {
- if (*pixels++ == colorkey)
- {
- mask[y * bpl + x / 8] &= ~(1 << (7 - x % 8));
- }
- }
- }
- break;
- }
- case 2:
- {
- for (S32 y = 0; y < height; ++y)
- {
- Uint16* pixels = (Uint16*)icon->pixels + y * picth / 2;
- for (S32 x = 0; x < width; ++x)
- {
- if (*pixels++ == colorkey)
- {
- mask[y * bpl + x / 8] &= ~(1 << (7 - x % 8));
- }
- }
- }
- break;
- }
- case 4:
- {
- for (S32 y = 0; y < height; ++y)
- {
- Uint32* pixels = (Uint32*)icon->pixels + y * picth / 4;
- for (S32 x = 0; x < width; ++x)
- {
- if (*pixels++ == colorkey)
- {
- mask[y * bpl + x / 8] &= ~(1 << (7 - x % 8));
- }
- }
- }
- break;
- }
- default: // Invalid icon ?... Return an opaque mask
- return mask;
- }
- // We need the mask as given, except in LSBfirst format instead of
- // MSBfirst. Reverse the bits in each byte.
- Uint8* lsb_mask = (Uint8*)malloc(mask_len);
- if (!lsb_mask)
- {
- free(mask);
- return NULL;
- }
- memset((void*)lsb_mask, 0, mask_len);
- for (S32 i = 0; i < mask_len; ++i)
- {
- Uint8 x = mask[i];
- x = (x & 0xaa) >> 1 | (x & 0x55) << 1;
- x = (x & 0xcc) >> 2 | (x & 0x33) << 2;
- x = (x & 0xf0) >> 4 | (x & 0x0f) << 4;
- lsb_mask[i] = x;
- }
- free(mask);
- return lsb_mask;
- }
- #endif
- // Opaque mask.
- return mask;
- }
- // This method must be called at the end of createContext() so that
- // mSDL_Display and mSDL_XWindowID got initialized...
- void LLWindowSDL::setWindowIcon()
- {
- #if 0 // Does not seem to make any difference...
- // Give an icon name hint to X11
- static const char* icon_hint = "cvlv_icon";
- XTextProperty icon_prop;
- XStringListToTextProperty((char**)&icon_hint, 1, &icon_prop);
- XSetWMIconName(mSDL_Display, mSDL_XWindowID, &icon_prop);
- XFree(icon_prop.value);
- #endif
- // Set the application icon.
- S32 icon_size = 48;
- char* envvar = getenv("LL_WINDOW_ICON_SIZE");
- if (envvar)
- {
- icon_size = atoi(envvar);
- if (icon_size != 32 && icon_size != 48 && icon_size != 64 &&
- icon_size != 128 && icon_size != 256)
- {
- icon_size = 48;
- }
- }
- std::string icon_name = llformat("cvlv_icon%d.bmp", icon_size);
- SDL_Surface* icon = load_bmp_resource(icon_name.c_str());
- if (!icon)
- {
- return;
- }
- #if WINDOW_ICON_TANSPARENCY
- // This attempts to give a black-keyed mask to the icon.
- SDL_SetColorKey(icon, SDL_TRUE, SDL_MapRGB(icon->format, 0, 0, 0));
- #endif
- bool success = false;
- // Reimplementation of SDL1's SDL_WM_SetIcon() for X11
- do
- {
- // *FIXME: 32 bits is what *appears* to be needed, although the default
- // visual depth is 24 bits... There is some voodoo magic performed by
- // SDL1 with available visuals and their possible depths to determine
- // the right pixel format, but implementing it seems quite an overkill:
- // 32 bits *should* work for everyone ! HB
- S32 bpp = 32;
- // And just in case, the LL_WINDOW_ICON_BPP environment variable allows
- // to change that value at runtime...
- envvar = getenv("LL_WINDOW_ICON_BPP");
- if (envvar)
- {
- bpp = atoi(envvar);
- if (bpp != 32 && bpp != 24 && bpp != 16 && bpp != 15)
- {
- // We would not deal with those... Let's try and fallback to
- // SDL_SetWindowIcon() for them.
- break;
- }
- llinfos << "Using " << bpp
- << " bits per pixel for the window icon." << llendl;
- }
- // Various X11 data needed below...
- S32 screen = DefaultScreen(mSDL_Display);
- S32 default_depth = DefaultDepth(mSDL_Display, screen);
- if (default_depth == 8)
- {
- break;
- }
- Visual* default_visual = DefaultVisual(mSDL_Display, screen);
- S32 root_window = RootWindow(mSDL_Display, screen);
- S32 width = icon->w;
- S32 height = icon->h;
- SDL_Surface* sicon = SDL_CreateRGBSurface(0, width, height, bpp,
- default_visual->red_mask,
- default_visual->green_mask,
- default_visual->blue_mask,
- 0);
- if (!sicon)
- {
- break;
- }
- // At this point, I skip all the 8 bits depth conversion code, since we
- // excluded this case above already... HB
- SDL_Rect bounds;
- bounds.x = 0;
- bounds.y = 0;
- bounds.w = width;
- bounds.h = height;
- if (SDL_LowerBlit(icon, &bounds, sicon, &bounds) != 0)
- {
- SDL_FreeSurface(sicon);
- break;
- }
- // Generate a mask.
- Uint8* mask = generate_icon_mask(icon);
- if (!mask)
- {
- SDL_FreeSurface(sicon);
- break;
- }
- Pixmap mask_pixmap = XCreatePixmapFromBitmapData(mSDL_Display,
- mSDL_XWindowID,
- (char*)mask,
- width, height,
- 1L, 0L, 1);
- free(mask);
- // Transfer the image to an X11 pixmap
- XImage* icon_image = XCreateImage(mSDL_Display, default_visual,
- default_depth, ZPixmap, 0,
- (char*)sicon->pixels, width, height,
- 32, 0);
- if (!icon_image)
- {
- SDL_FreeSurface(sicon);
- break;
- }
- #if LL_BIG_ENDIAN
- icon_image->byte_order = MSBFirst;
- #else
- icon_image->byte_order = LSBFirst;
- #endif
- Pixmap icon_pixmap = XCreatePixmap(mSDL_Display, root_window,
- width, height, default_depth);
- XGCValues gc_values;
- GC gc = XCreateGC(mSDL_Display, icon_pixmap, 0, &gc_values);
- XPutImage(mSDL_Display, icon_pixmap, gc, icon_image, 0, 0, 0, 0,
- width, height);
- XFreeGC(mSDL_Display, gc);
- sicon->pixels = NULL;
- // I skip the SDL_VIDEO_X11_ICONWIN fix here, which was only for use
- // with some old buggy versions of Enlightenment... HB
- // Set the window icon to the icon pixmap and associated mask
- XWMHints* wmhints = XAllocWMHints();
- wmhints->flags = IconPixmapHint | IconMaskHint | InputHint;
- wmhints->icon_pixmap = icon_pixmap;
- wmhints->icon_mask = mask_pixmap;
- wmhints->input = True;
- XSetWMHints(mSDL_Display, mSDL_XWindowID, wmhints);
- XFree(wmhints);
- XSync(mSDL_Display, False);
- success = true;
- }
- while (false);
- // Fallback code, using SDL2's SDL_SetWindowIcon()
- if (!success)
- {
- // SDL2's SDL_SetWindowIcon() fails to set the application menu button
- // in the title bar (and Meta-TAB window cycler, unless the BMP icon
- // size is exactly 32x32 pixels for the latter... Go figure !), while
- // SDL1's SDL_WM_SetIcon() works fine in this respect ! :-( HB
- SDL_Surface* icon2 = load_bmp_resource("cvlv_icon32.bmp");
- if (icon2)
- {
- SDL_FreeSurface(icon);
- icon = icon2;
- #if WINDOW_ICON_TANSPARENCY
- // This attempts to give a black-keyed mask to the icon.
- SDL_SetColorKey(icon, SDL_TRUE, SDL_MapRGB(icon->format, 0, 0, 0));
- #endif
- }
- SDL_SetWindowIcon(mWindow, icon);
- }
- SDL_FreeSurface(icon);
- }
- bool LLWindowSDL::createContext(S32 x, S32 y, S32 width, S32 height, S32 bits,
- bool fullscreen, bool disable_vsync)
- {
- llinfos << "Fullscreen = " << fullscreen << " - Size = " << width << "x"
- << height << llendl;
- // Captures do not survive contexts
- mGrabbyKeyFlags = 0;
- mCaptured = false;
- if (SDL_Init(SDL_INIT_VIDEO) < 0)
- {
- llinfos << "sdl_init() failed ! " << SDL_GetError() << llendl;
- setupFailure("sdl_init() failure, window creation error");
- return false;
- }
- SDL_version c_sdl_version;
- SDL_VERSION(&c_sdl_version);
- llinfos << "Compiled against SDL " << S32(c_sdl_version.major) << "."
- << S32(c_sdl_version.minor) << "." << S32(c_sdl_version.patch)
- << llendl;
- SDL_version r_sdl_version;
- SDL_GetVersion(&r_sdl_version);
- llinfos << "Running against SDL " << S32(r_sdl_version.major) << "."
- << S32(r_sdl_version.minor) << "." << S32(r_sdl_version.patch)
- << llendl;
- SDL_DisplayMode dm;
- if (SDL_GetDesktopDisplayMode(0, &dm) == 0)
- {
- if (dm.h > 0)
- {
- mOriginalAspectRatio = (F32)dm.w / (F32)dm.h;
- llinfos << "Original aspect ratio was " << dm.w << ":" << dm.h
- << " = " << mOriginalAspectRatio << llendl;
- }
- }
- if (width == 0)
- {
- width = 1024;
- }
- if (height == 0)
- {
- height = 768;
- }
- mFullscreen = fullscreen;
- mSDLFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
- #if 0
- if (fullscreen)
- {
- mSDLFlags |= SDL_WINDOW_FULLSCREEN;
- getFullScreenSize(width, height);
- }
- #endif
- GLint alpha_bits = 8;
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, alpha_bits);
- GLint red_bits = 8;
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, red_bits);
- GLint green_bits = 8;
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, green_bits);
- GLint blue_bits = 8;
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, blue_bits);
- GLint depth_bits = bits <= 16 ? 16 : 24;
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depth_bits);
- // Note: we need stencil support for a few (minor) things.
- GLint stencil_bits = 8;
- if (getenv("LL_GL_NO_STENCIL"))
- {
- stencil_bits = 0;
- }
- else
- {
- SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencil_bits);
- }
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- if (mFSAASamples > 0)
- {
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, mFSAASamples);
- }
- U32 context_flags = gDebugGL ? SDL_GL_CONTEXT_DEBUG_FLAG : 0;
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, context_flags);
- if (LLRender::sGLCoreProfile)
- {
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
- SDL_GL_CONTEXT_PROFILE_CORE);
- }
- // Request shared context support.
- SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
- mWindow = SDL_CreateWindow(mWindowTitle.c_str(), x, y, width, height,
- mSDLFlags);
- if (!mWindow)
- {
- llwarns << "Window creation failure. SDL error: " << SDL_GetError()
- << llendl;
- setupFailure("Window creation failure.");
- return false;
- }
- // Clear the window (in the two buffers)
- glClearColor(0.f, 0.f, 0.f, 1.f);
- glClear(GL_COLOR_BUFFER_BIT);
- SDL_GL_SwapWindow(mWindow);
- glClearColor(0.f, 0.f, 0.f, 1.f);
- glClear(GL_COLOR_BUFFER_BIT);
- if (mFullscreen)
- {
- #if 0 // This should be OK, now that we forbid changes during sessions. HB
- if (LLRender::sGLCoreProfile)
- {
- // Full screen mode crashes the viewer when core GL profile is
- // enabled... Use full desktop mode instead. HB
- gUseFullDesktop = true;
- }
- #endif
- U32 flags = gUseFullDesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP
- : SDL_WINDOW_FULLSCREEN;
- mFullscreen = SDL_SetWindowFullscreen(mWindow, flags) == 0;
- if (!mFullscreen && !gUseFullDesktop)
- {
- gUseFullDesktop = true; // For next time...
- llwarns << "Failed to set real full screen mode, trying full desktop mode..."
- << llendl;
- flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
- mFullscreen = SDL_SetWindowFullscreen(mWindow, flags) == 0;
- }
- if (!mFullscreen)
- {
- llwarns << "Failure to set up full screen window " << width << "x"
- << height << llendl;
- }
- }
- if (mFullscreen)
- {
- llinfos << "Setting up fullscreen " << width << "x" << height
- << llendl;
- SDL_DisplayMode target_mode;
- SDL_zero(target_mode);
- target_mode.w = width;
- target_mode.h = height;
- SDL_DisplayMode closest_mode;
- SDL_zero(closest_mode);
- SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(mWindow),
- &target_mode, &closest_mode);
- if (SDL_SetWindowDisplayMode(mWindow, &closest_mode) == 0)
- {
- SDL_DisplayMode mode;
- SDL_GetWindowDisplayMode(mWindow, &mode);
- mFullscreenWidth = mode.w;
- mFullscreenHeight = mode.h;
- mFullscreenBits = SDL_BITSPERPIXEL(mode.format);
- mFullscreenRefresh = mode.refresh_rate;
- llinfos << "Running at " << mFullscreenWidth << "x"
- << mFullscreenHeight << "x" << mFullscreenBits << " @ "
- << mFullscreenRefresh << "Hz" << llendl;
- }
- else
- {
- llwarns << "Fullscreen creation failure. SDL error: "
- << SDL_GetError() << llendl;
- // No fullscreen support
- mFullscreen = false;
- mFullscreenWidth = -1;
- mFullscreenHeight = -1;
- mFullscreenBits = -1;
- mFullscreenRefresh = -1;
- SDL_SetWindowFullscreen(mWindow, 0);
- SDL_SetWindowResizable(mWindow, SDL_TRUE);
- std::string error =
- llformat("Unable to run fullscreen at %d x %d.", width,
- height);
- setupFailure(error);
- return false;
- }
- }
- if (LLRender::sGLCoreProfile)
- {
- U32 major = 4;
- U32 minor = 6;
- while (true)
- {
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
- mContext = SDL_GL_CreateContext(mWindow);
- if (mContext)
- {
- llinfos << "Activated core GL profile v" << major << "."
- << minor << llendl;
- break; // Success !
- }
- if (minor > 0)
- {
- --minor;
- }
- else if (major == 4)
- {
- // Continue from 3.3 downwards
- major = minor = 3;
- }
- else
- {
- break; // Failed to set core GL profile...
- }
- }
- }
- else
- {
- mContext = SDL_GL_CreateContext(mWindow);
- }
- if (!mContext)
- {
- llwarns << "Cannot create GL context: " << SDL_GetError() << llendl;
- setupFailure("GL context creation error");
- return false;
- }
- // Detect video memory size.
- S32 vram_mb = x11_detect_vram_kb() / 1024;
- if (vram_mb > 0 && !gGLManager.mVRAM)
- {
- gGLManager.mVRAM = vram_mb;
- }
- // If VRAM is not detected, that is handled later
- SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &red_bits);
- SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &green_bits);
- SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blue_bits);
- SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alpha_bits);
- SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth_bits);
- SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &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;
- GLint color_bits = red_bits + green_bits + blue_bits + alpha_bits;
- // *FIXME: actually it is 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 (color_bits < 32)
- {
- close();
- setupFailure(
- "Second Life requires True Color (32 bits) to run in a window.\n"
- "Please go to Control Panels -> Display -> Settings and\n"
- "set the screen to 32 bits color.\n"
- "Alternately, if you choose to run fullscreen, Second Life\n"
- "will automatically adjust the screen each time it runs.");
- return false;
- }
- // Grab the window manager specific information
- SDL_SysWMinfo info;
- SDL_VERSION(&info.version);
- 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.window;
- }
- else
- {
- llwarns << "We are not running under X11 !" << llendl;
- }
- }
- else
- {
- llwarns << "We are not running under any known WM !" << llendl;
- }
- // Set the application icon.
- setWindowIcon();
- SDL_StartTextInput();
- // Make sure multisampling is disabled by default
- glDisable(GL_MULTISAMPLE);
- // We do not need to get the current gamma, since there is a call that
- // restores it to the system defaults.
- return true;
- }
- // Changes fullscreen resolution, or switches between windowed and fullscreen
- // modes.
- //virtual
- bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen& size,
- bool disable_vsync,
- const LLCoordScreen* const posp)
- {
- llinfos << "Fullscreen: " << (fullscreen ? "yes" : "no") << llendl;
- // Just nuke the context and start over.
- destroyContext();
- bool result = createContext(0, 0, size.mX, size.mY, 32, fullscreen,
- disable_vsync);
- if (result)
- {
- if (!gGLManager.initGL())
- {
- setupFailure("Could not properly reinitialize OpenGL !");
- }
- // Start with arrow cursor
- initCursors();
- setCursor(UI_CURSOR_ARROW);
- }
- stop_glerror();
- return result;
- }
- struct LLSharedOpenGLContext
- {
- SDL_GLContext mContext;
- };
- //virtual
- void* LLWindowSDL::createSharedContext()
- {
- LLSharedOpenGLContext* context = new LLSharedOpenGLContext;
- context->mContext = SDL_GL_CreateContext(mWindow);
- if (context->mContext)
- {
- // Do not use VSYNC on any shared context since they are not used for
- // actual rendering. HB
- SDL_GL_SetSwapInterval(0);
- }
- // Make our main (renderer) context current again (even if the new context
- // creation failed, since SDL2 documentation does not say anything about
- // what happens to the current context selection in this case). HB
- SDL_GL_MakeCurrent(mWindow, mContext);
- if (!context->mContext)
- {
- // Something went (very) wrong... Free the structure and return a NULL
- // pointer to signify we do not have a GL context available. HB
- llwarns_sparse << "Failed to create a new shared GL context."
- << llendl;
- delete context;
- return NULL;
- }
- // *HACK: this will cause a proper screen refresh by triggering a full
- // redraw event at the SDL level. Without this hack, you get a "blocky"
- // UI until SDL receives a redraw event (which may take seconds). HB
- refresh();
- return (void*)context;
- }
- //virtual
- void LLWindowSDL::makeContextCurrent(void* context)
- {
- if (context)
- {
- SDL_GL_MakeCurrent(mWindow,
- ((LLSharedOpenGLContext*)context)->mContext);
- }
- else
- {
- // Restore main GL thread context.
- SDL_GL_MakeCurrent(mWindow, mContext);
- }
- }
- //virtual
- void LLWindowSDL::destroySharedContext(void* context)
- {
- if (context) // Ignore attempts to destroy invalid contexts. HB
- {
- LLSharedOpenGLContext* sc = (LLSharedOpenGLContext*)context;
- SDL_GL_DeleteContext(sc->mContext);
- delete sc;
- }
- }
- //virtual
- void LLWindowSDL::destroyContext()
- {
- SDL_StopTextInput();
- mSDL_Display = NULL;
- mSDL_XWindowID = None;
- // Clean up remaining GL state before blowing away window
- llinfos << "Shutting down GL..." << llendl;
- gGLManager.shutdownGL();
- if (mContext)
- {
- llinfos << "Destroying context..." << llendl;
- SDL_GL_DeleteContext(mContext);
- mContext = NULL;
- }
- if (mWindow)
- {
- llinfos << "Destroying window..." << llendl;
- SDL_DestroyWindow(mWindow);
- }
- mWindow = NULL;
- llinfos << "Quitting SDL video sub-system..." << llendl;
- SDL_QuitSubSystem(SDL_INIT_VIDEO);
- }
- // Destroys all OS-specific code associated with a window. Usually called from
- // LLWindow::destroyWindow()
- //virtual
- void LLWindowSDL::close()
- {
- #if 0 // Is window is already closed ?
- if (!mWindow)
- {
- return;
- }
- #endif
- // Make sure cursor is visible and we have not mangled the clipping state
- setMouseClipping(false);
- showCursor();
- destroyContext();
- }
- //virtual
- void LLWindowSDL::minimize()
- {
- if (mWindow)
- {
- SDL_MinimizeWindow(mWindow);
- }
- }
- //virtual
- void LLWindowSDL::restore()
- {
- if (mWindow)
- {
- SDL_RestoreWindow(mWindow);
- }
- }
- //virtual
- bool LLWindowSDL::getVisible()
- {
- return mWindow && (SDL_GetWindowFlags(mWindow) & SDL_WINDOW_SHOWN);
- }
- //virtual
- bool LLWindowSDL::getMinimized()
- {
- return mWindow && (SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MINIMIZED);
- }
- //virtual
- void LLWindowSDL::calculateBordersOffsets()
- {
- if (!mWindow || mFullscreen)
- {
- return;
- }
- int x, y;
- SDL_GetWindowPosition(mWindow, &x, &y);
- mPosOffsetX = x - mInitialPosX;
- mPosOffsetY = y - mInitialPosY;
- // *TODO: make the max border size configurable ? The 25 and 50 fixed
- // values should cover all themes, but who knows ?...
- if (mPosOffsetX < 0 || mPosOffsetY < 0 || mPosOffsetX > 25 ||
- mPosOffsetX > 50)
- {
- // This could happen if the window manager overrides the position of
- // the window or lets the user move it around on creation. In this
- // case, we must report a 0,0 position with getPosition() (since the
- // saved window position would not be used anyway, in such a window
- // manager configuration). HB
- llwarns << "Incoherent window borders offsets found: x = "
- << mPosOffsetX << " - y = " << mPosOffsetY
- << ". Did you move the window on creation ? Window position will always been reported as 0,0."
- << llendl;
- mPosOffsetX = mPosOffsetY = -1; // Flag for "report 0,0"
- }
- else
- {
- llinfos << "Window borders offsets: x = " << mPosOffsetX << " - y = "
- << mPosOffsetY << llendl;
- }
- }
- //virtual
- bool LLWindowSDL::getPosition(LLCoordScreen* position)
- {
- if (position && mWindow)
- {
- // Problem: the window coordinates returned by SDL_GetWindowPosition()
- // are offset by the size of the window borders (but only after the
- // window got fully decorated by the window manager, which happens
- // some time after it is created), while SDL_CreateWindow() needs the
- // absolute coordinates... Since getPosition() is used on viewer exit,
- // wrong coordinates would be stored in saved settings.
- // This is the reason why we need the mPosOffsetX and mPosOffsetY
- // offsets, computing them with calculateBordersOffsets() above just
- // after the window is decorated (i.e. before the user would have a
- // chance to move it manually). HB
- int x, y;
- SDL_GetWindowPosition(mWindow, &x, &y);
- #if 0 // Saddly, this does not work, or at least not with all window managers
- // (I always seen it return top = left = 0)... HB
- int top, left;
- if (SDL_GetWindowBordersSize(mWindow, &top, &left, NULL, NULL) == 0)
- {
- x -= left;
- y -= top;
- }
- #else
- // Report a 0,0 (like with SDL1) position if we do not know what the
- // borders offsets are or if in full screen mode.
- if (mFullscreen || mPosOffsetX < 0)
- {
- position->mX = position->mY = 0;
- }
- else
- {
- position->mX = x - mPosOffsetX;
- position->mY = y - mPosOffsetY;
- }
- #endif
- return true;
- }
- return false;
- }
- //virtual
- bool LLWindowSDL::getSize(LLCoordScreen* size)
- {
- if (mWindow && size)
- {
- SDL_GetWindowSize(mWindow, &size->mX, &size->mY);
- return true;
- }
- return false;
- }
- //virtual
- bool LLWindowSDL::getSize(LLCoordWindow* size)
- {
- if (mWindow && size)
- {
- SDL_GetWindowSize(mWindow, &size->mX, &size->mY);
- return true;
- }
- return false;
- }
- //virtual
- bool LLWindowSDL::setSize(const LLCoordScreen size)
- {
- if (!mWindow)
- {
- return false;
- }
- if (SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MAXIMIZED)
- {
- SDL_RestoreWindow(mWindow);
- }
- SDL_SetWindowSize(mWindow, size.mX, size.mY);
- SDL_Event event;
- event.type = SDL_WINDOWEVENT;
- event.window.event = SDL_WINDOWEVENT_RESIZED;
- event.window.windowID = SDL_GetWindowID(mWindow);
- event.window.data1 = size.mX;
- event.window.data2 = size.mY;
- SDL_PushEvent(&event);
- return true;
- }
- // *HACK: this method causes a proper screen refresh by triggering a full
- // redraw event at the SDL level. HB
- //virtual
- void LLWindowSDL::refresh()
- {
- LLCoordScreen size;
- if (getSize(&size))
- {
- setSize(size);
- }
- }
- //virtual
- void LLWindowSDL::swapBuffers()
- {
- if (mWindow)
- {
- LL_FAST_TIMER(FTM_SWAP);
- SDL_GL_SwapWindow(mWindow);
- }
- }
- //virtual
- bool LLWindowSDL::restoreGamma()
- {
- if (!mCustomGammaSet)
- {
- return true;
- }
- mCustomGammaSet = false;
- return mWindow && SDL_SetWindowGammaRamp(mWindow, mPrevGammaRamp[0],
- mPrevGammaRamp[1],
- mPrevGammaRamp[2]) == 0;
- }
- //virtual
- bool LLWindowSDL::setGamma(F32 gamma)
- {
- LL_DEBUGS("Window") << "Setting gamma to " << gamma << LL_ENDL;
- mCurrentGamma = llclamp(gamma, 0.01f, 10.f);
- if (!mWindow)
- {
- return false;
- }
- // Get the previous gamma ramp to restore later.
- if (!mCustomGammaSet)
- {
- if (SDL_GetWindowGammaRamp(mWindow, mPrevGammaRamp[0],
- mPrevGammaRamp[1], mPrevGammaRamp[2]) != 0)
- {
- llwarns << "Failed to get the previous gamma ramp." << llendl;
- // Use a gamma ramp with default gamma = 1.f
- SDL_CalculateGammaRamp(1.f, mPrevGammaRamp[0]);
- SDL_CalculateGammaRamp(1.f, mPrevGammaRamp[1]);
- SDL_CalculateGammaRamp(1.f, mPrevGammaRamp[2]);
- }
- mCustomGammaSet = true;
- }
- SDL_CalculateGammaRamp(mCurrentGamma, mCurrentGammaRamp);
- return SDL_SetWindowGammaRamp(mWindow, mCurrentGammaRamp,
- mCurrentGammaRamp, mCurrentGammaRamp) == 0;
- }
- // Constrains the mouse to the window.
- //virtual
- void LLWindowSDL::setMouseClipping(bool b)
- {
- #if 0
- SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
- #endif
- }
- //virtual
- bool LLWindowSDL::setCursorPosition(const LLCoordWindow& position)
- {
- LLCoordScreen screen_pos;
- if (!convertCoords(position, &screen_pos))
- {
- return false;
- }
- // Do the actual forced cursor move.
- if (mWindow)
- {
- SDL_WarpMouseInWindow(mWindow, screen_pos.mX, screen_pos.mY);
- }
- return true;
- }
- //virtual
- bool LLWindowSDL::getCursorPosition(LLCoordWindow* position)
- {
- // Point cursor_point
- LLCoordScreen screen_pos;
- int x, y;
- SDL_GetMouseState(&x, &y);
- screen_pos.mX = x;
- screen_pos.mY = y;
- return convertCoords(screen_pos, position);
- }
- //virtual
- 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;
- #endif
- // MBW: there are a couple of bad assumptions here. One is that the display
- // list would not include ridiculous resolutions nobody would ever use. The
- // other is that the list is in order.
- // New assumptions:
- // - pixels are square (the only reasonable choice, really)
- // - The user runs their display at a native resolution, so the resolution
- // of the display when the app is launched has an aspect ratio that
- // matches the monitor.
- // RN: actually, the assumption that there are no ridiculous resolutions
- // (above the display's native capabilities) has been born out in my
- // experience.
- // Pixels are often not square (just ask the people who run their LCDs at
- // 1024x768 or 800x600 when running fullscreen, like me).
- // The ordering of display list is a blind assumption though, so we should
- // check for max values.
- // Things might be different on the Mac though, so I'll defer to MBW
- // 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.
- return mOverrideAspectRatio > 0.f ? mOverrideAspectRatio
- : mOriginalAspectRatio;
- }
- //virtual
- F32 LLWindowSDL::getPixelAspectRatio()
- {
- F32 pixel_aspect = 1.f;
- if (getFullscreen())
- {
- LLCoordScreen screen_size;
- if (getSize(&screen_size))
- {
- pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY /
- (F32)screen_size.mX;
- }
- }
- return pixel_aspect;
- }
- // This is to support 'temporarily windowed' mode so that dialogs are still
- // usable in fullscreen.
- //virtual
- void LLWindowSDL::beforeDialog()
- {
- llinfos << "called" << llendl;
- if (SDLReallyCaptureInput(false)) // Must un-grab input so popup works !
- {
- if (mFullscreen)
- {
- // Need to temporarily go non-fullscreen; bless SDL for providing a
- // SDL_WM_ToggleFullScreen() - though it only works in X11
- if (mSDL_XWindowID != None && mWindow)
- {
- SDL_SetWindowFullscreen(mWindow, 0);
- }
- }
- }
- if (mSDL_Display)
- {
- // Everything that we/SDL asked for should happen before we potentially
- // hand control over to GTK.
- XSync(mSDL_Display, False);
- }
- }
- //virtual
- void LLWindowSDL::afterDialog()
- {
- llinfos << "called." << llendl;
- if (mFullscreen)
- {
- // Need to restore fullscreen mode after dialog; only works in X11
- if (mSDL_XWindowID != None && mWindow)
- {
- SDL_SetWindowFullscreen(mWindow, 0);
- }
- }
- }
- // Sets/reset the XWMHints flag for 'urgency' that usually makes the icon flash
- void LLWindowSDL::x11_set_urgent(bool urgent)
- {
- if (mSDL_Display && !mFullscreen)
- {
- LL_DEBUGS("Window") << "X11 hint for urgency, " << urgent << LL_ENDL;
- XWMHints* 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);
- }
- }
- //virtual
- void LLWindowSDL::flashIcon(F32 seconds)
- {
- LL_DEBUGS("Window") << "Flashing icon for " << seconds << " seconds"
- << LL_ENDL;
- F32 remaining_time = mFlashTimer.getRemainingTimeF32();
- if (remaining_time < seconds)
- {
- remaining_time = seconds;
- }
- mFlashTimer.reset();
- mFlashTimer.setTimerExpirySec(remaining_time);
- x11_set_urgent(true);
- mFlashing = true;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Clipboards (primary and secondary) implementation (c)2015 Henri Beauchamp
- ///////////////////////////////////////////////////////////////////////////////
- static Atom XA_CLIPBOARD;
- static Atom XA_COMPOUND_TEXT;
- static Atom XA_UTF8_STRING;
- static Atom XA_TARGETS;
- static Atom PVT_PASTE_BUFFER;
- // Filters through SDL_Events searching for clipboard requests from the X
- // server. evt is the event to filter.
- static int x11_clipboard_filter(void*, SDL_Event* evt)
- {
- Display* display = LLWindowSDL::getSDLDisplay();
- if (!display) return 1;
- // We are only interested in window manager events
- if (evt->type == SDL_SYSWMEVENT)
- {
- XEvent xevent = evt->syswm.msg->msg.x11.event;
- // See if the event is a selection/clipboard request
- if (xevent.type == SelectionRequest)
- {
- // Get the request in question
- XSelectionRequestEvent* request = &xevent.xselectionrequest;
- if (!request) return 1; // Paranoia
- LL_DEBUGS("Window") << "Caught event type SelectionRequest. Request target: "
- << request->target << " - Selection type: "
- << request->selection << LL_ENDL;
- // Generate a reply to the selection request
- XSelectionEvent reply;
- reply.type = SelectionNotify;
- reply.serial = xevent.xany.serial;
- reply.send_event = xevent.xany.send_event;
- reply.display = display;
- reply.requestor = request->requestor;
- reply.selection = request->selection;
- reply.target = request->target;
- reply.property = request->property;
- reply.time = request->time;
- // They want to know what we can provide/offer
- if (request->target == XA_TARGETS)
- {
- LL_DEBUGS("Window") << "Request is XA_TARGETS" << LL_ENDL;
- Atom possibleTargets[] =
- {
- XA_STRING,
- XA_UTF8_STRING,
- XA_COMPOUND_TEXT
- };
- XChangeProperty(display, request->requestor, request->property,
- XA_ATOM, 32, PropModeReplace,
- (unsigned char*)possibleTargets, 3);
- }
- // They want a string (all we can provide)
- else if (request->target == XA_STRING ||
- request->target == XA_UTF8_STRING ||
- request->target == XA_COMPOUND_TEXT)
- {
- std::string utf8;
- if (request->selection == XA_PRIMARY)
- {
- LL_DEBUGS("Window") << "Primary selection request"
- << LL_ENDL;
- utf8 =
- wstring_to_utf8str(sWindowImplementation->getPrimaryText());
- }
- else
- {
- LL_DEBUGS("Window") << "Clipboard request" << LL_ENDL;
- utf8 =
- wstring_to_utf8str(sWindowImplementation->getSecondaryText());
- }
- XChangeProperty(display, request->requestor, request->property,
- request->target, 8, PropModeReplace,
- (unsigned char*)utf8.c_str(), utf8.length());
- }
- else if (request->selection == XA_CLIPBOARD)
- {
- LL_DEBUGS("Window") << "Unhandled request" << LL_ENDL;
- // Did not have what they wanted, so no property set
- reply.property = None;
- }
- else
- {
- LL_DEBUGS("Window") << "Unknown selection request. Ignoring."
- << LL_ENDL;
- return 1;
- }
- // Dispatch the event
- XSendEvent(request->display, request->requestor, False,
- NoEventMask, (XEvent*)&reply);
- XSync(display, False);
- }
- else if (xevent.type == SelectionClear)
- {
- // Get the request in question
- XSelectionRequestEvent* request = &xevent.xselectionrequest;
- if (!request) return 1; // Paranoia
- // We no longer own the clipboard: clear our stored data.
- if (request->selection == XA_PRIMARY)
- {
- LL_DEBUGS("Window") << "Someone else took the ownership of the primary selection; clearing our primary selection buffer."
- << LL_ENDL;
- sWindowImplementation->clearPrimaryText();
- }
- else if (request->selection == XA_CLIPBOARD)
- {
- LL_DEBUGS("Window") << "Someone else took the ownership of the clipboard; clearing our clipboard buffer."
- << LL_ENDL;
- sWindowImplementation->clearSecondaryText();
- }
- }
- }
- return 1;
- }
- void LLWindowSDL::initialiseX11Clipboard()
- {
- if (mSDL_Display)
- {
- LL_DEBUGS("Window") << "Initializing the X11 clipboard" << LL_ENDL;
- // Register the event filter
- SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
- SDL_SetEventFilter(x11_clipboard_filter, NULL);
- // Get the clipboard atom (it is not defined by default)
- XA_CLIPBOARD = XInternAtom(mSDL_Display, "CLIPBOARD", False);
- // Get the compound text type atom
- XA_COMPOUND_TEXT = XInternAtom(mSDL_Display, "COMPOUND_TEXT", False);
- // UTF-8 string atom
- XA_UTF8_STRING = XInternAtom(mSDL_Display, "UTF8_STRING", False);
- // TARGETS atom
- XA_TARGETS = XInternAtom(mSDL_Display, "TARGETS", False);
- // SL_PASTE_BUFFER atom
- PVT_PASTE_BUFFER = XInternAtom(mSDL_Display, "SL_PASTE_BUFFER", False);
- }
- }
- static bool grab_property(Display* display, Window window, Atom selection,
- Atom target)
- {
- XDeleteProperty(display, window, PVT_PASTE_BUFFER);
- XFlush(display);
- XConvertSelection(display, selection, target, PVT_PASTE_BUFFER, window,
- CurrentTime);
- // We now need to wait for a response from the window that owns the
- // clipboard.
- LL_DEBUGS("Window") << "Waiting for the selection owner to provide its text..."
- << LL_ENDL;
- SDL_Event event;
- XEvent xevent;
- Uint32 start = SDL_GetTicks();
- constexpr Uint32 maxticks = 1000; // 1 second
- bool response = false;
- while (!response && SDL_GetTicks() - start < maxticks)
- {
- // 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;
- // See if it is a response to our request
- if (xevent.type == SelectionNotify &&
- xevent.xselection.requestor == window)
- {
- response = true;
- }
- }
- }
- bool ret = response && xevent.xselection.property != None;
- LL_DEBUGS("Window") << "... " << (ret ? "Succeeded" : "Failed") << " !"
- << LL_ENDL;
- return ret;
- }
- bool LLWindowSDL::getSelectionText(Atom selection, LLWString& text)
- {
- if (!mSDL_Display) return false;
- // Get the owner of the clipboard selection.
- Window owner = XGetSelectionOwner(mSDL_Display, selection);
- if (owner == None)
- {
- // Only the primary selection may be owned by None, in the cut buffer
- // (legacy, xterm way of dealing with selections). Else, it means the
- // selection is empty...
- if (selection != XA_PRIMARY)
- {
- // The clipboard is empty...
- text.clear();
- return false;
- }
- LL_DEBUGS("Window") << "No owner for current selection. Using default root window and XA_CUT_BUFFER0"
- << LL_ENDL;
- owner = DefaultRootWindow(mSDL_Display);
- selection = XA_CUT_BUFFER0;
- }
- // Ask the window that currently owns the clipboard to convert it
- LL_DEBUGS("Window") << "Requesting conversion to XA_UTF8_STRING"
- << LL_ENDL;
- if (!grab_property(mSDL_Display, mSDL_XWindowID, selection,
- XA_UTF8_STRING))
- {
- #if 0 // We do not support ISO 2022 encoding in the viewer, so...
- LL_DEBUGS("Window") << "Requesting conversion to XA_COMPOUND_TEXT"
- << LL_ENDL;
- if (!grab_property(mSDL_Display, mSDL_XWindowID, selection,
- XA_COMPOUND_TEXT))
- #endif
- {
- LL_DEBUGS("Window") << "Requesting conversion to XA_STRING"
- << LL_ENDL;
- if (!grab_property(mSDL_Display, mSDL_XWindowID, selection,
- XA_STRING))
- {
- // The clipboard does not contains any valid text.
- text.clear();
- return false;
- }
- }
- }
- // Recover any paste buffer text (using S32_MAX for max length).
- Atom type;
- int format = 0;
- unsigned long len = 0;
- unsigned long remaining = 0;
- unsigned char* data = NULL;
- int res = XGetWindowProperty(mSDL_Display, mSDL_XWindowID,
- PVT_PASTE_BUFFER, 0, S32_MAX, False,
- AnyPropertyType, &type, &format, &len,
- &remaining, &data);
- if (data && len)
- {
- if (format == 8)
- {
- std::string tmp;
- tmp.assign((const char*)data);
- text = LLWString(utf8str_to_wstring(tmp));
- }
- else
- {
- // This should not happen since we asked above for 8 bits encoding
- // conversions only...
- llwarns << "Unsupported clipboard text format type: "
- << format << " bits characters instead of 8." << llendl;
- len = 0;
- }
- }
- if (!len)
- {
- text.clear();
- }
- // Note: XGetWindowProperty() is documented as always allocating at least
- // one "extra byte", even if the property is zero-length, so we need to
- // free any allocated data, even for len == 0.
- if (data)
- {
- XFree(data);
- }
- return res == Success;
- }
- bool LLWindowSDL::setSelectionText(Atom selection, const LLWString& text)
- {
- std::string utf8 = wstring_to_utf8str(text);
- if (selection == XA_PRIMARY)
- {
- // Copy the text into the root window's cut buffer
- XStoreBytes(mSDL_Display, utf8.c_str(), utf8.length() + 1);
- mPrimaryClipboard = text;
- LL_DEBUGS("Window") << "Setting the primary selection text" << LL_ENDL;
- }
- else
- {
- mSecondaryClipboard = text;
- LL_DEBUGS("Window") << "Setting the clipboard text" << LL_ENDL;
- }
- // Set ourself as the owner of the selection atom
- XSetSelectionOwner(mSDL_Display, selection, mSDL_XWindowID, CurrentTime);
- // Check if we acquired ownership or not
- Window owner = XGetSelectionOwner(mSDL_Display, selection);
- return owner == mSDL_XWindowID;
- }
- //virtual
- bool LLWindowSDL::isClipboardTextAvailable()
- {
- return mSDL_Display &&
- XGetSelectionOwner(mSDL_Display, XA_CLIPBOARD) != None;
- }
- //virtual
- bool LLWindowSDL::pasteTextFromClipboard(LLWString& text)
- {
- return getSelectionText(XA_CLIPBOARD, text);
- }
- //virtual
- bool LLWindowSDL::copyTextToClipboard(const LLWString& text)
- {
- return setSelectionText(XA_CLIPBOARD, text);
- }
- //virtual
- bool LLWindowSDL::isPrimaryTextAvailable()
- {
- if (mSDL_Display)
- {
- LLWString text;
- return getSelectionText(XA_PRIMARY, text);
- }
- return false; // failure
- }
- //virtual
- bool LLWindowSDL::pasteTextFromPrimary(LLWString& text)
- {
- return getSelectionText(XA_PRIMARY, text);
- }
- //virtual
- bool LLWindowSDL::copyTextToPrimary(const LLWString& text)
- {
- return setSelectionText(XA_PRIMARY, text);
- }
- ///////////////////////////////////////////////////////////////////////////////
- //virtual
- LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32& num_resolutions)
- {
- if (!mSupportedResolutions)
- {
- mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
- mNumSupportedResolutions = 0;
- // *TODO: get count from display corresponding to mWindow
- S32 count = llclamp(0, (S32)SDL_GetNumDisplayModes(0),
- MAX_NUM_RESOLUTIONS);
- for (S32 i = 0; i < count; ++i)
- {
- SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
- if (SDL_GetDisplayMode(0 , i, &mode) != 0)
- {
- continue;
- }
- S32 w = mode.w;
- S32 h = mode.h;
- if (w >= 800 && h >= 600)
- {
- // Make sure we do not add the same resolution multiple times !
- bool resolution_exists = false;
- for (S32 i = 0; i < mNumSupportedResolutions; ++i)
- {
- if (mSupportedResolutions[i].mWidth == w &&
- mSupportedResolutions[i].mHeight == h)
- {
- resolution_exists = true;
- break;
- }
- }
- if (!resolution_exists)
- {
- mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
- mSupportedResolutions[mNumSupportedResolutions++].mHeight = h;
- }
- }
- }
- }
- num_resolutions = mNumSupportedResolutions;
- return mSupportedResolutions;
- }
- //virtual
- bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow* to)
- {
- if (to && mWindow)
- {
- to->mX = from.mX;
- S32 height;
- SDL_GetWindowSize(mWindow, NULL, &height);
- to->mY = height - from.mY - 1;
- return true;
- }
- return false;
- }
- //virtual
- bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
- {
- if (to && mWindow)
- {
- to->mX = from.mX;
- S32 height;
- SDL_GetWindowSize(mWindow, NULL, &height);
- to->mY = height - from.mY - 1;
- return true;
- }
- return false;
- }
- //virtual
- bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
- {
- if (to)
- {
- // In the fullscreen case, window and screen coordinates are the same.
- to->mX = from.mX;
- to->mY = from.mY;
- return true;
- }
- return false;
- }
- //virtual
- bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen* to)
- {
- if (to)
- {
- // In the fullscreen case, window and screen coordinates are the same.
- to->mX = from.mX;
- to->mY = from.mY;
- return true;
- }
- return false;
- }
- //virtual
- bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL* to)
- {
- LLCoordWindow wcoord;
- return convertCoords(from, &wcoord) && convertCoords(wcoord, to);
- }
- //virtual
- bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen* to)
- {
- LLCoordWindow wcoord;
- return convertCoords(from, &wcoord) && convertCoords(wcoord, to);
- }
- void LLWindowSDL::setupFailure(const std::string& text)
- {
- destroyContext();
- OSMessageBox(text);
- }
- bool LLWindowSDL::SDLReallyCaptureInput(bool capture)
- {
- mCaptured = capture;
- bool newgrab = capture;
- // Only bother if we are windowed
- if (!mFullscreen && mSDL_Display)
- {
- // We dirtily mix raw X11 with SDL so that our pointer is not (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.
- if (capture)
- {
- newgrab = XGrabPointer(mSDL_Display, mSDL_XWindowID, True, 0,
- GrabModeAsync, GrabModeAsync, None, None,
- CurrentTime) == GrabSuccess;
- }
- else
- {
- XUngrabPointer(mSDL_Display, CurrentTime);
- // Make sure the ungrab happens RIGHT NOW.
- XSync(mSDL_Display, False);
- newgrab = false;
- }
- }
- // Return boolean success for whether we ended up in the desired state
- return capture == newgrab;
- }
- 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 spoil their
- // day by acquiring the exclusive X11 mouse lock for as long as ALT is held
- // down, so the window manager cannot easily see what is happening. Tested
- // successfully with Metacity. And... do the same with CTRL, for other darn
- // WMs. We do not care about other metakeys as SL does not use them with
- // dragging (for now).
- // We maintain a bitmap of critical keys which are up and down instead of
- // simply key-counting, because SDL sometimes reports misbalanced
- // keyup/keydown event pairs to us for whatever reason.
- 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;
- }
- if (gain)
- {
- mGrabbyKeyFlags |= mask;
- }
- else
- {
- mGrabbyKeyFlags &= ~mask;
- }
- // 0 means we do not need to mousegrab, otherwise grab.
- return mGrabbyKeyFlags;
- }
- //virtual
- void LLWindowSDL::gatherInput()
- {
- constexpr Uint32 CLICK_THRESHOLD = 300; // milliseconds
- static S32 left_click = 0;
- static S32 right_click = 0;
- static Uint32 last_left_down = 0;
- static Uint32 last_right_down = 0;
- SDL_Event event;
- // Handle all outstanding SDL events
- while (SDL_PollEvent(&event))
- {
- switch (event.type)
- {
- case SDL_MOUSEWHEEL:
- {
- if (event.wheel.y != 0)
- {
- mCallbacks->handleScrollWheel(this, -event.wheel.y);
- }
- break;
- }
- case SDL_MOUSEMOTION:
- {
- LLCoordWindow win_coord(event.button.x, event.button.y);
- LLCoordGL open_gl_coord;
- convertCoords(win_coord, &open_gl_coord);
- MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
- mCallbacks->handleMouseMove(this, open_gl_coord, mask);
- break;
- }
- case SDL_TEXTINPUT:
- {
- auto str = utf8str_to_utf16str(event.text.text);
- mKeyVirtualKey = str[0];
- mKeyModifiers = SDL_GetModState();
- MASK mask = gKeyboardp ? gKeyboardp->currentMask(false)
- : 0;
- for (const auto& key : str)
- {
- handleUnicodeUTF16(key, mask);
- }
- break;
- }
- case SDL_KEYDOWN:
- {
- mKeyVirtualKey = event.key.keysym.sym;
- mKeyModifiers = event.key.keysym.mod;
- if (mKeyVirtualKey == SDLK_KP_ENTER)
- {
- mKeyVirtualKey = SDLK_RETURN; // For CEF to get it right
- }
- if (gKeyboardp)
- {
- gKeyboardp->handleKeyDown(mKeyVirtualKey, mKeyModifiers);
- }
- // Because, with SDL2, RETURN (and key pad ENTER, but it was
- // already turned into SDLK_RETURN above) is not part of the
- // text characters sent via the SDL_TEXTINPUT event, in excess
- // of handleKeyDown() we also must invoke handleUnicodeUTF16()
- // with the SDLK_RETURN virtual key, since this is where the
- // viewer code deals with RETURN/ENTER...
- if (mKeyVirtualKey == SDLK_RETURN)
- {
- MASK mask = gKeyboardp ? gKeyboardp->currentMask(false)
- : 0;
- handleUnicodeUTF16(SDLK_RETURN, mask);
- }
- // Part of the fix for SL-13243
- if (SDLCheckGrabbyKeys(event.key.keysym.sym, true) != 0)
- {
- SDLReallyCaptureInput(true);
- }
- break;
- }
- case SDL_KEYUP:
- {
- mKeyVirtualKey = event.key.keysym.sym;
- if (mKeyVirtualKey == SDLK_KP_ENTER)
- {
- mKeyVirtualKey = SDLK_RETURN; // For CEF to get it right
- }
- mKeyModifiers = event.key.keysym.mod;
- // Part of the fix for SL-13243
- if (SDLCheckGrabbyKeys(mKeyVirtualKey, false) == 0)
- {
- SDLReallyCaptureInput(false);
- }
- if (gKeyboardp)
- {
- gKeyboardp->handleKeyUp(mKeyVirtualKey, mKeyModifiers);
- }
- break;
- }
- case SDL_MOUSEBUTTONDOWN:
- {
- bool is_double_click = false;
- LLCoordWindow win_coord(event.button.x, event.button.y);
- LLCoordGL open_gl_coord;
- convertCoords(win_coord, &open_gl_coord);
- MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
- if (event.button.button == SDL_BUTTON_LEFT)
- {
- // SDL does not manage double clicking...
- Uint32 now = SDL_GetTicks();
- if (now - last_left_down > CLICK_THRESHOLD)
- {
- left_click = 1;
- }
- else if (++left_click >= 2)
- {
- left_click = 0;
- is_double_click = true;
- }
- last_left_down = now;
- }
- else if (event.button.button == SDL_BUTTON_RIGHT)
- {
- Uint32 now = SDL_GetTicks();
- if (now - last_right_down > CLICK_THRESHOLD)
- {
- right_click = 1;
- }
- else if (++right_click >= 2)
- {
- right_click = 0;
- is_double_click = true;
- }
- last_right_down = now;
- }
- if (event.button.button == SDL_BUTTON_LEFT) // left
- {
- if (is_double_click)
- {
- mCallbacks->handleDoubleClick(this, open_gl_coord,
- mask);
- }
- else
- {
- mCallbacks->handleMouseDown(this, open_gl_coord, mask);
- }
- }
- else if (event.button.button == SDL_BUTTON_RIGHT) // right
- {
- mCallbacks->handleRightMouseDown(this, open_gl_coord, mask);
- }
- else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
- {
- mCallbacks->handleMiddleMouseDown(this, open_gl_coord, 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 win_coord(event.button.x, event.button.y);
- LLCoordGL open_gl_coord;
- convertCoords(win_coord, &open_gl_coord);
- MASK mask = gKeyboardp ? gKeyboardp->currentMask(true) : 0;
- if (event.button.button == SDL_BUTTON_LEFT)
- {
- mCallbacks->handleMouseUp(this, open_gl_coord, mask);
- }
- else if (event.button.button == SDL_BUTTON_RIGHT)
- {
- mCallbacks->handleRightMouseUp(this, open_gl_coord, mask);
- }
- else if (event.button.button == SDL_BUTTON_MIDDLE)
- {
- mCallbacks->handleMiddleMouseUp(this, open_gl_coord, mask);
- }
- // Do not handle mousewheel here...
- break;
- }
- case SDL_WINDOWEVENT:
- {
- Uint8 ev = event.window.event;
- if (ev == SDL_WINDOWEVENT_FOCUS_GAINED)
- {
- mCallbacks->handleFocus(this);
- }
- else if (ev == SDL_WINDOWEVENT_FOCUS_LOST)
- {
- mCallbacks->handleFocusLost(this);
- }
- else if (ev == SDL_WINDOWEVENT_RESIZED)
- {
- if (!mWindow)
- {
- // *FIXME: 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;
- }
- S32 width = event.window.data1;
- S32 height = event.window.data2;
- LL_DEBUGS("Window") << "Handling a resize event: " << width
- << "x" << height << LL_ENDL;
- if (gFrameSleepTime > 0)
- {
- // *HACK: clear the window to black to avoid a white
- // flickering when resizing the viewer window while not
- // logged in or when yieding each frame to the OS, due
- // to the volontary throttling of the frame rate with
- // ms_sleep(), and only in this case. Sadly, and unlike
- // SLD1, SDL2 clears the screen to white (instead of
- // black) on resize, so if we do not redraw immediately
- // after a resize event, we get a rather flashy and
- // totally ugly flickering effect. HB
- glClearColor(0.f, 0.f, 0.f, 1.f);
- glClear(GL_COLOR_BUFFER_BIT);
- }
- mCallbacks->handleResize(this, width, height);
- }
- else if (ev == SDL_WINDOWEVENT_RESTORED)
- {
- bool minimized = SDL_GetWindowFlags(mWindow) &
- SDL_WINDOW_MINIMIZED;
- llinfos << "SDL minimized state switched to "
- << !minimized << llendl;
- mCallbacks->handleActivate(this, !minimized);
- }
- else if (ev == SDL_WINDOWEVENT_EXPOSED)
- {
- // Repaint the whole window.
- S32 width = 0;
- S32 height = 0;
- SDL_GetWindowSize(mWindow, &width, &height);
- mCallbacks->handlePaint(this, 0, 0, width, height);
- }
- 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;
- }
- default:
- {
- LL_DEBUGS("Window") << "Unhandled SDL event type "
- << event.type << LL_ENDL;
- }
- }
- }
- // 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;
- }
- }
- //virtual
- void LLWindowSDL::setCursor(ECursorType cursor)
- {
- if (mCursorFrozen)
- {
- return;
- }
- if (cursor == UI_CURSOR_ARROW && mBusyCount > 0)
- {
- cursor = UI_CURSOR_WORKING;
- }
- if (mCurrentCursor != cursor)
- {
- 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;
- }
- }
- static SDL_Cursor* sdl_cursor_from_bmp(const char* fname, int hotx = 0,
- int hoty = 0)
- {
- SDL_Cursor* sdlcursor = NULL;
- // Load cursor pixel data from BMP file
- SDL_Surface* bmpsurface = load_bmp_resource(fname);
- if (bmpsurface && bmpsurface->w % 8 == 0)
- {
- LL_DEBUGS("Window") << "Loaded cursor file " << fname << " "
- << bmpsurface->w << "x" << bmpsurface->h
- << LL_ENDL;
- SDL_Surface* 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 bits RGBA surface so we
- // only have to cope with processing one type of pixel format.
- if (SDL_BlitSurface(bmpsurface, NULL, cursurface, NULL) == 0)
- {
- // NB: 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);
- // 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 (S32 i = 0; i < cursurface->h; ++i)
- {
- for (S32 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] |= ((U8)data_bit) << (7 - (j & 7));
- cursor_mask[bit_offset] |= ((U8)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 for: " << fname << llendl;
- }
- SDL_FreeSurface(cursurface);
- SDL_FreeSurface(bmpsurface);
- }
- else
- {
- llwarns << "Cursor load failure for: " << fname << llendl;
- }
- return sdlcursor;
- }
- void LLWindowSDL::initCursors()
- {
- // Blank the cursor pointer array for those we may miss.
- for (S32 i = 0; i < UI_CURSOR_COUNT; ++i)
- {
- mSDLCursors[i] = NULL;
- }
- // Pre-make an SDL cursor for each of the known cursor types. We hardcode
- // the hotspots - to avoid that we would have to write a .cur file loader.
- // NOTE: SDL does not load RLE-compressed BMP files.
- if (getenv("LL_USE_SYSTEM_CURSORS"))
- {
- // Use the user's theme cursors where possible.
- mSDLCursors[UI_CURSOR_ARROW] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
- mSDLCursors[UI_CURSOR_WAIT] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
- mSDLCursors[UI_CURSOR_HAND] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
- mSDLCursors[UI_CURSOR_IBEAM] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
- mSDLCursors[UI_CURSOR_CROSS] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
- mSDLCursors[UI_CURSOR_SIZENWSE] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
- mSDLCursors[UI_CURSOR_SIZENESW] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
- mSDLCursors[UI_CURSOR_SIZEWE] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
- mSDLCursors[UI_CURSOR_SIZENS] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
- mSDLCursors[UI_CURSOR_NO] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
- mSDLCursors[UI_CURSOR_WORKING] =
- SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW);
- }
- else // Use our custom cursors instead.
- {
- mSDLCursors[UI_CURSOR_ARROW] = sdl_cursor_from_bmp("llarrow.bmp");
- mSDLCursors[UI_CURSOR_WAIT] = sdl_cursor_from_bmp("wait.bmp", 12, 15);
- mSDLCursors[UI_CURSOR_HAND] = sdl_cursor_from_bmp("hand.bmp", 7, 10);
- mSDLCursors[UI_CURSOR_IBEAM] = sdl_cursor_from_bmp("ibeam.bmp",
- 15, 16);
- mSDLCursors[UI_CURSOR_CROSS] = sdl_cursor_from_bmp("cross.bmp",
- 16, 14);
- mSDLCursors[UI_CURSOR_SIZENWSE] = sdl_cursor_from_bmp("sizenwse.bmp",
- 14, 17);
- mSDLCursors[UI_CURSOR_SIZENESW] = sdl_cursor_from_bmp("sizenesw.bmp",
- 17, 17);
- mSDLCursors[UI_CURSOR_SIZEWE] = sdl_cursor_from_bmp("sizewe.bmp",
- 16, 14);
- mSDLCursors[UI_CURSOR_SIZENS] = sdl_cursor_from_bmp("sizens.bmp",
- 17, 16);
- mSDLCursors[UI_CURSOR_NO] = sdl_cursor_from_bmp("llno.bmp", 8, 8);
- mSDLCursors[UI_CURSOR_WORKING] = sdl_cursor_from_bmp("working.bmp",
- 12, 15);
- }
- mSDLCursors[UI_CURSOR_TOOLGRAB] = sdl_cursor_from_bmp("lltoolgrab.bmp",
- 2, 13);
- mSDLCursors[UI_CURSOR_TOOLLAND] = sdl_cursor_from_bmp("lltoolland.bmp",
- 1, 6);
- mSDLCursors[UI_CURSOR_TOOLFOCUS] = sdl_cursor_from_bmp("lltoolfocus.bmp",
- 8, 5);
- mSDLCursors[UI_CURSOR_TOOLCREATE] = sdl_cursor_from_bmp("lltoolcreate.bmp",
- 7, 7);
- mSDLCursors[UI_CURSOR_ARROWDRAG] = sdl_cursor_from_bmp("arrowdrag.bmp");
- mSDLCursors[UI_CURSOR_ARROWCOPY] = sdl_cursor_from_bmp("arrowcop.bmp");
- mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] =
- sdl_cursor_from_bmp("llarrowdragmulti.bmp");
- mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] =
- sdl_cursor_from_bmp("arrowcopmulti.bmp", 0, 0);
- mSDLCursors[UI_CURSOR_NOLOCKED] = sdl_cursor_from_bmp("llnolocked.bmp",
- 8, 8);
- mSDLCursors[UI_CURSOR_ARROWLOCKED] =
- sdl_cursor_from_bmp("llarrowlocked.bmp");
- mSDLCursors[UI_CURSOR_GRABLOCKED] = sdl_cursor_from_bmp("llgrablocked.bmp",
- 2, 13);
- mSDLCursors[UI_CURSOR_TOOLTRANSLATE] =
- sdl_cursor_from_bmp("lltooltranslate.bmp");
- mSDLCursors[UI_CURSOR_TOOLROTATE] =
- sdl_cursor_from_bmp("lltoolrotate.bmp");
- mSDLCursors[UI_CURSOR_TOOLSCALE] = sdl_cursor_from_bmp("lltoolscale.bmp");
- mSDLCursors[UI_CURSOR_TOOLCAMERA] = sdl_cursor_from_bmp("lltoolcamera.bmp",
- 7, 5);
- mSDLCursors[UI_CURSOR_TOOLPAN] = sdl_cursor_from_bmp("lltoolpan.bmp",
- 7, 5);
- mSDLCursors[UI_CURSOR_TOOLZOOMIN] = sdl_cursor_from_bmp("lltoolzoomin.bmp",
- 7, 5);
- mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] =
- sdl_cursor_from_bmp("toolpickobject3.bmp");
- mSDLCursors[UI_CURSOR_TOOLSIT] = sdl_cursor_from_bmp("toolsit.bmp");
- mSDLCursors[UI_CURSOR_TOOLBUY] = sdl_cursor_from_bmp("toolbuy.bmp");
- mSDLCursors[UI_CURSOR_TOOLPAY] = sdl_cursor_from_bmp("toolpay.bmp");
- mSDLCursors[UI_CURSOR_TOOLOPEN] = sdl_cursor_from_bmp("toolopen.bmp");
- mSDLCursors[UI_CURSOR_TOOLPLAY] = sdl_cursor_from_bmp("toolplay.bmp");
- mSDLCursors[UI_CURSOR_TOOLPAUSE] = sdl_cursor_from_bmp("toolpause.bmp");
- mSDLCursors[UI_CURSOR_TOOLMEDIAOPEN] =
- sdl_cursor_from_bmp("toolmediaopen.bmp");
- mSDLCursors[UI_CURSOR_PIPETTE] = sdl_cursor_from_bmp("lltoolpipette.bmp",
- 2, 28);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING] =
- sdl_cursor_from_bmp("lltoolpathfinding.bmp", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_START] =
- sdl_cursor_from_bmp("lltoolpathfindingpathstart.bmp", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD] =
- sdl_cursor_from_bmp("lltoolpathfindingpathstartadd.bmp", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END] =
- sdl_cursor_from_bmp("lltoolpathfindingpathend.bmp", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD] =
- sdl_cursor_from_bmp("lltoolpathfindingpathendadd.bmp", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLNO] = sdl_cursor_from_bmp("llno.bmp", 8, 8);
- }
- void LLWindowSDL::quitCursors()
- {
- if (mWindow)
- {
- for (S32 i = 0; i < UI_CURSOR_COUNT; ++i)
- {
- if (mSDLCursors[i])
- {
- SDL_FreeCursor(mSDLCursors[i]);
- mSDLCursors[i] = NULL;
- }
- }
- }
- else
- {
- // SDL does not 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 (S32 i = 0; i < UI_CURSOR_COUNT; ++i)
- {
- mSDLCursors[i] = NULL;
- }
- }
- }
- //virtual
- 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.
- LL_DEBUGS("Window") << "called" << LL_ENDL;
- }
- //virtual
- void LLWindowSDL::releaseMouse()
- {
- // See comment in LWindowSDL::captureMouse()
- LL_DEBUGS("Window") << "called" << LL_ENDL;
- }
- //virtual
- void LLWindowSDL::hideCursor()
- {
- if (!mCursorHidden)
- {
- mCursorHidden = true;
- mHideCursorPermanent = true;
- SDL_ShowCursor(0);
- }
- }
- //virtual
- void LLWindowSDL::showCursor()
- {
- if (mCursorHidden)
- {
- mCursorHidden = false;
- mHideCursorPermanent = false;
- SDL_ShowCursor(1);
- }
- }
- //virtual
- void LLWindowSDL::showCursorFromMouseMove()
- {
- if (!mHideCursorPermanent)
- {
- showCursor();
- }
- }
- //virtual
- void LLWindowSDL::hideCursorUntilMouseMove()
- {
- if (!mHideCursorPermanent)
- {
- hideCursor();
- mHideCursorPermanent = false;
- }
- }
- // Make the raw keyboard data available - used to poke through to CEF so that
- // the embedded browser has access to the virtual keycodes etc that it needs.
- //virtual
- LLSD LLWindowSDL::getNativeKeyData()
- {
- LLSD result = LLSD::emptyMap();
- result["virtual_key"] = (S32)mKeyVirtualKey;
- // Genuine native SDL modifiers.
- result["sdl_modifiers"] = (S32)mKeyModifiers;
- return result;
- }
- // Open a URL with the user's default web browser.
- // Must begin with protocol identifier.
- //virtual
- void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async)
- {
- bool found = false;
- for (S32 i = 0; i < gURLProtocolWhitelistCount; ++i)
- {
- if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos)
- {
- found = true;
- break;
- }
- }
- if (!found)
- {
- llwarns << "spawn_web_browser called for url with protocol not on whitelist: "
- << escaped_url << llendl;
- return;
- }
- llinfos << "Spawning browser with URL: " << escaped_url << llendl;
- if (mSDL_Display)
- {
- // Just in case - before forking.
- XSync(mSDL_Display, False);
- }
- std::string cmd = gDirUtil.getAppRODataDir() + "/bin/launch_url.sh";
- std::string arg = escaped_url;
- exec_cmd(cmd, arg);
- llinfos << "Returned from web browser launch." << llendl;
- }
- //virtual
- void* LLWindowSDL::getPlatformWindow()
- {
- return NULL;
- }
- // This method is currently used when we are 'launched' via an SLURL or, with
- // SDL2, before taking snapshots (so that the latter does not get ruined with
- // dirty (unrefreshed) rectangles due to other overlapping windows). HB
- //virtual
- void LLWindowSDL::bringToFront()
- {
- if (mWindow)
- {
- llinfos << "Bringing viewer window to front" << llendl;
- SDL_RaiseWindow(mWindow);
- }
- if (mFullscreen || !mSDL_Display)
- {
- return;
- }
- llinfos << "Bringing viewer window to front" << llendl;
- // We must find the frame window Id used by the window manager, but if we
- // cannot find any WM window frame, use our own window... HB
- Window wm_window = mSDL_XWindowID;
- Window root, parent;
- Window* childlist = NULL;
- unsigned int num_children;
- int res = XQueryTree(mSDL_Display, mSDL_XWindowID, &root, &parent,
- &childlist, &num_children);
- if (res && parent && parent != mSDL_XWindowID)
- {
- wm_window = parent;
- llinfos << "Found WM frame window Id: " << wm_window << llendl;
- }
- if (childlist)
- {
- XFree(childlist);
- }
- // Now raise the frame to the top of the window stack (which will raise all
- // its chidren with it, our own root window included).
- XRaiseWindow(mSDL_Display, wm_window);
- XMapRaised(mSDL_Display, wm_window);
- // This could be needed by some window managers which would ignore the X11
- // request, on the condition they are configured to raise focused windows
- // on top... HB
- XEvent event = { 0 };
- event.xclient.type = ClientMessage;
- event.xclient.serial = 0;
- event.xclient.send_event = True;
- event.xclient.message_type = XInternAtom(mSDL_Display,
- "_NET_ACTIVE_WINDOW", False);
- event.xclient.window = wm_window;
- event.xclient.format = 32;
- XSendEvent(mSDL_Display, DefaultRootWindow(mSDL_Display), False,
- SubstructureRedirectMask | SubstructureNotifyMask, &event);
- XSync(mSDL_Display, False);
- }
- //static
- std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList()
- {
- // Use libfontconfig to find us a nice ordered list of fallback fonts
- // specific to this system.
- std::string final_fallback = "/usr/share/fonts/TTF/dejavu/DejaVuSans.ttf";
- // Fonts are expensive in the current system, do not enumerate too many of
- // them
- constexpr size_t max_font_count_cutoff = 40;
- // Our 'ideal' font properties which define the sorting results.
- // slant=0 means Roman, index=0 means the first face in a font file (the
- // one we actually use), weight=80 means medium weight, spacing=0 means
- // proportional spacing.
- std::string sort_order("slant=0:index=0:weight=80:spacing=0");
- // elide_unicode_coverage removes fonts from the list whose unicode range
- // is covered by fonts earlier in the list. This usually removes ~90% of
- // the fonts as redundant (which is great because the font list can be
- // huge), but might unnecessarily reduce the renderable range if for some
- // reason our FreeType actually fails to use some of the fonts we want it
- // to.
- constexpr bool elide_unicode_coverage = true;
- std::vector<std::string> rtns;
- FcFontSet* fs = NULL;
- FcPattern* sortpat = NULL;
- llinfos << "Getting system font list from FontConfig..." << llendl;
- // If the user has a system-wide language preference, then favor fonts from
- // that language group. This does not affect the types of languages that
- // can be displayed, but ensures that their preferred language is rendered
- // from a single consistent font where possible.
- FL_Locale* locale = NULL;
- FL_Success success = FL_FindLocale(&locale, FL_MESSAGES);
- if (success != 0)
- {
- if (success >= 2 && locale->lang) // confident!
- {
- llinfos << "Language " << locale->lang << llendl;
- llinfos << "Location " << locale->country << llendl;
- llinfos << "Variant " << locale->variant << llendl;
- llinfos << "Preferring fonts of language: " << locale->lang
- << llendl;
- sort_order = "lang=" + std::string(locale->lang) + ":" + sort_order;
- }
- }
- FL_FreeLocale(&locale);
- if (!FcInit())
- {
- llwarns << "FontConfig failed to initialize." << llendl;
- rtns.emplace_back(final_fallback);
- return rtns;
- }
- sortpat = FcNameParse((FcChar8*)sort_order.c_str());
- if (sortpat)
- {
- // Sort the list of system fonts from most-to-least-desirable.
- FcResult result;
- fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, NULL, &result);
- FcPatternDestroy(sortpat);
- }
- int found_font_count = 0;
- if (fs)
- {
- // Get the full pathnames to the fonts, where available, which is what
- // we really want.
- std::string lc_name;
- found_font_count = fs->nfont;
- for (int i = 0; i < fs->nfont; ++i)
- {
- FcChar8* filename;
- if (FcResultMatch == FcPatternGetString(fs->fonts[i], FC_FILE, 0,
- &filename)
- && filename)
- {
- std::string name((const char*)filename);
- lc_name = name;
- LLStringUtil::toLower(lc_name);
- size_t len = lc_name.length();
- if (lc_name.rfind(".pcf") == len - 4 ||
- lc_name.rfind(".pcf.gz") == len - 7)
- {
- // Not a true type font, skip it !
- LL_DEBUGS("Window") << name
- << " is a bitmap font, skipping..."
- << LL_ENDL;
- continue;
- }
- rtns.emplace_back(name);
- if (rtns.size() >= max_font_count_cutoff)
- {
- break; // hit limit
- }
- }
- }
- FcFontSetDestroy(fs);
- }
- LL_DEBUGS("Window") << "Using font list: ";
- for (size_t i = 0, count = rtns.size(); i < count; ++i)
- {
- LL_CONT << " " << rtns[i];
- }
- LL_CONT << LL_ENDL;
- llinfos << "Using " << rtns.size() << "/" << found_font_count
- << " system fonts." << llendl;
- rtns.emplace_back(final_fallback);
- return rtns;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Splash screen implementation (c)2020-2021 Henri Beauchamp
- ///////////////////////////////////////////////////////////////////////////////
- class HBSplashScreenSDLImpl
- {
- public:
- HBSplashScreenSDLImpl();
- ~HBSplashScreenSDLImpl();
- void show();
- void hide();
- void update(const std::string& msg);
- private:
- Display* mDisplay;
- Window mWindow;
- XColor mShadow;
- GC mGC;
- S32 mScreen;
- S32 mWidth;
- S32 mHeight;
- S32 mTextXOffset;
- S32 mTextYOffset;
- };
- // Default splash screen size. Must be smaller than the splash background that
- // contains an icon on the left: if the splash pixmap can be created, its size
- // will be used instead.
- #define SPLASH_WIDTH 220
- #define SPLASH_HEIGHT 50
- // Offsets for the text in the splash screen
- #define SPLASH_TEXT_X_OFFSET 15
- #define SPLASH_TEXT_Y_OFFSET 28
- HBSplashScreenSDLImpl::HBSplashScreenSDLImpl()
- : mWidth(SPLASH_WIDTH),
- mHeight(SPLASH_HEIGHT),
- mTextXOffset(SPLASH_TEXT_X_OFFSET),
- mTextYOffset(SPLASH_TEXT_Y_OFFSET)
- {
- // Open a connection to the X11 server
- mDisplay = XOpenDisplay(NULL);
- if (!mDisplay)
- {
- llwarns << "Could not open the X11 display !" << llendl;
- return;
- }
- mScreen = DefaultScreen(mDisplay);
- S32 root_window = RootWindow(mDisplay, mScreen);
- // Try to create a pixmap from our splash bitmap file
- Pixmap splash_pixmap = {};
- bool has_spash_pixmap = false;
- while (true)
- {
- SDL_Surface* splash = load_bmp_resource("splash.bmp");
- if (!splash)
- {
- llwarns << "Could not load the splash background." << llendl;
- break;
- }
- Visual* default_visual = DefaultVisual(mDisplay, mScreen);
- S32 width = splash->w;
- S32 height = splash->h;
- SDL_Surface* surf = SDL_CreateRGBSurface(0, width, height, 32,
- default_visual->red_mask,
- default_visual->green_mask,
- default_visual->blue_mask, 0);
- if (!surf)
- {
- llwarns << "Could not create the splash surface." << llendl;
- SDL_FreeSurface(splash);
- break;
- }
- SDL_Rect bounds;
- bounds.x = 0;
- bounds.y = 0;
- bounds.w = width;
- bounds.h = height;
- if (SDL_LowerBlit(splash, &bounds, surf, &bounds) != 0)
- {
- llwarns << "Could not blit the splash surface." << llendl;
- SDL_FreeSurface(splash);
- SDL_FreeSurface(surf);
- break;
- }
- S32 default_depth = DefaultDepth(mDisplay, mScreen);
- XImage* splash_img = XCreateImage(mDisplay, default_visual,
- default_depth, ZPixmap, 0,
- (char*)surf->pixels,
- width, height, 32, 0);
- if (!splash_img)
- {
- llwarns << "Could not create the splash image." << llendl;
- SDL_FreeSurface(splash);
- SDL_FreeSurface(surf);
- break;
- }
- #if LL_BIG_ENDIAN
- splash_img->byte_order = MSBFirst;
- #else
- splash_img->byte_order = LSBFirst;
- #endif
- splash_pixmap = XCreatePixmap(mDisplay, root_window, width, height,
- default_depth);
- XGCValues gc_values;
- GC gc = XCreateGC(mDisplay, splash_pixmap, 0, &gc_values);
- XPutImage(mDisplay, splash_pixmap, gc, splash_img, 0, 0, 0, 0,
- width, height);
- XFreeGC(mDisplay, gc);
- surf->pixels = NULL;
- SDL_FreeSurface(splash);
- SDL_FreeSurface(surf);
- // We have a splash background, adjust the window size and text offsets
- // accordingly.
- has_spash_pixmap = true;
- // Assuming the difference is the icon width on left:
- mTextXOffset += width - mWidth;
- // Half the difference since we want the text vertically centered
- mTextYOffset += (height - mHeight) / 2;
- mWidth = width;
- mHeight = height;
- break;
- }
- // *TODO: use a nice and adequately sized font...
- // Create a custom color map for the grey background.
- Colormap colormap = DefaultColormap(mDisplay, mScreen);
- XColor grey;
- XParseColor(mDisplay, colormap, "#D8D8D8", &grey);
- XAllocColor(mDisplay, colormap, &grey);
- // Create an X11 window
- mWindow = XCreateSimpleWindow(mDisplay, root_window, 0, 0, mWidth, mHeight,
- 0, BlackPixel(mDisplay, mScreen),
- grey.pixel);
- if (!mWindow)
- {
- llwarns << "Could not open an X11 window !" << llendl;
- XCloseDisplay(mDisplay);
- mDisplay = NULL;
- return;
- }
- // Specify the type of window (splash screen).
- // Note: the splash screen should automatically be centered by the window
- // manager, based on the _NET_WM_WINDOW_TYPE_SPLASH property set here. It
- // will also not have any window decoration.
- Atom type = XInternAtom(mDisplay, "_NET_WM_WINDOW_TYPE", False);
- Atom value = XInternAtom(mDisplay, "_NET_WM_WINDOW_TYPE_SPLASH", False);
- XChangeProperty(mDisplay, mWindow, type, XA_ATOM, 32, PropModeReplace,
- reinterpret_cast<unsigned char*>(&value), 1);
- if (has_spash_pixmap)
- {
- XSetWindowBackgroundPixmap(mDisplay, mWindow, splash_pixmap);
- mGC = NULL;
- }
- else
- {
- llwarns << "Could not create the background pixmap. The icon will be missing from the splash."
- << llendl;
- // Create a custom GC for drawing the 3D borders in update()
- unsigned long valuemask = 0;
- XGCValues values;
- mGC = XCreateGC(mDisplay, mWindow, valuemask, &values);
- if (mGC)
- {
- XSetLineAttributes(mDisplay, mGC, 1, LineSolid, CapButt,
- JoinBevel);
- // Color for the shadow line in the 3D borders.
- XParseColor(mDisplay, colormap, "#606060", &mShadow);
- XAllocColor(mDisplay, colormap, &mShadow);
- }
- else
- {
- llwarns << "Could not create a graphics context. The borders will be missing from the splash."
- << llendl;
- }
- }
- // Select the only event we care for (Expose)
- XSelectInput(mDisplay, mWindow, ExposureMask);
- }
- HBSplashScreenSDLImpl::~HBSplashScreenSDLImpl()
- {
- hide();
- }
- void HBSplashScreenSDLImpl::show()
- {
- if (mDisplay)
- {
- XMapWindow(mDisplay, mWindow);
- // Wait for the window to be displayed
- XEvent e;
- while (!XCheckTypedEvent(mDisplay, Expose, &e)) ;
- }
- }
- void HBSplashScreenSDLImpl::hide()
- {
- if (mDisplay)
- {
- if (mGC)
- {
- XFreeGC(mDisplay, mGC);
- }
- XUnmapWindow(mDisplay, mWindow);
- XDestroyWindow(mDisplay, mWindow);
- XFlush(mDisplay);
- XCloseDisplay(mDisplay);
- mDisplay = NULL;
- }
- }
- void HBSplashScreenSDLImpl::update(const std::string& msg)
- {
- if (!mDisplay) return;
- // Clear old contents
- XClearWindow(mDisplay, mWindow);
- // Draw the text itself
- XDrawString(mDisplay, mWindow, DefaultGC(mDisplay, mScreen),
- mTextXOffset, mTextYOffset, msg.c_str(), msg.size());
- // Draw a 3D-like border around the window when we could not create a
- // background from the pixmap...
- if (mGC)
- {
- // First, a black "outer" rectangle (to ensure proper contrast when
- // the window is drawn above a light background).
- XSetForeground(mDisplay, mGC, BlackPixel(mDisplay, mScreen));
- XDrawLine(mDisplay, mWindow, mGC, 0, 0, mWidth - 1, 0);
- XDrawLine(mDisplay, mWindow, mGC, mWidth - 1, 0,
- mWidth - 1, mHeight);
- XDrawLine(mDisplay, mWindow, mGC, mWidth, mHeight - 1,
- 0, mHeight - 1);
- XDrawLine(mDisplay, mWindow, mGC, 0, mHeight, 0, 0);
- // Then, the lower right, "inner" shadow corner
- XSetForeground(mDisplay, mGC, mShadow.pixel);
- XDrawLine(mDisplay, mWindow, mGC, mWidth - 2, 2,
- mWidth - 2, mHeight - 1);
- XDrawLine(mDisplay, mWindow, mGC, mWidth - 2,
- mHeight - 2, 2, mHeight - 2);
- // Finally, the upper left, "inner" lit corner
- XSetForeground(mDisplay, mGC, WhitePixel(mDisplay, mScreen));
- XDrawLine(mDisplay, mWindow, mGC, 1, mHeight - 2, 1, 1);
- XDrawLine(mDisplay, mWindow, mGC, 1, 1, mWidth - 2, 1);
- }
- XFlush(mDisplay);
- //sleep(1); // For debugging, to let some time to read it !
- }
- LLSplashScreenSDL::LLSplashScreenSDL()
- : mImpl(NULL)
- {
- // Since LLSplashScreen is invoked before creating the main window, we must
- // call this here ! HB
- LLWindowSDL::initXlibThreads();
- mImpl = new HBSplashScreenSDLImpl;
- }
- LLSplashScreenSDL::~LLSplashScreenSDL()
- {
- if (mImpl)
- {
- delete mImpl;
- mImpl = NULL;
- }
- }
- void LLSplashScreenSDL::showImpl()
- {
- if (mImpl)
- {
- mImpl->show();
- }
- }
- void LLSplashScreenSDL::updateImpl(const std::string& msg)
- {
- if (mImpl)
- {
- mImpl->update(msg);
- }
- }
- void LLSplashScreenSDL::hideImpl()
- {
- if (mImpl)
- {
- mImpl->hide();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Message box implementation (c)2015 Henri Beauchamp
- ///////////////////////////////////////////////////////////////////////////////
- S32 OSMessageBoxSDL(const std::string& text, const std::string& caption,
- U32 type)
- {
- #if 0 // Sadly, this does not work ("No message system available" or
- // "msgbox child process failed", depending on SDL2 version), and yes
- // I did also try with the "full" SDL_ShowMessageBox() function, with the
- // same result... HB
- // SDL2 implements a simple message box with just one OK button.
- // This is the only type currently used by the Linux viewer anyway.
- if (type == OSMB_OK)
- {
- std::string lc_caption = caption;
- LLStringUtil::toLower(lc_caption);
- U32 sdl_type;
- if (lc_caption.find("error") != std::string::npos)
- {
- sdl_type = SDL_MESSAGEBOX_ERROR;
- }
- else
- {
- sdl_type = SDL_MESSAGEBOX_INFORMATION;
- }
- S32 ret = SDL_ShowSimpleMessageBox(sdl_type, caption.c_str(),
- text.c_str(), NULL);
- if (ret == 0)
- {
- return OSBTN_OK;
- }
- llwarns << "Error creating SDL dialog: " << SDL_GetError() << llendl;
- // Else, fall-back to the generic message box code below...
- }
- #endif
- setenv("MESSAGE_BOX_CAPTION", caption.c_str(), 1);
- const std::string typestr = llformat("%d", type);
- setenv("MESSAGE_BOX_TYPE", typestr.c_str(), 1);
- std::string cmd = gDirUtil.getAppRODataDir() + "/bin/messagebox.sh";
- S32 ret = exec_cmd(cmd, text);
- if (ret == -1)
- {
- llwarns << "MSGBOX (" << type << "): " << caption << ": " << text
- << llendl;
- }
- return ret;
- }
- #endif // LL_LINUX
|