aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llwindow/llwindowsdl.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llwindow/llwindowsdl.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r--linden/indra/llwindow/llwindowsdl.cpp2506
1 files changed, 2506 insertions, 0 deletions
diff --git a/linden/indra/llwindow/llwindowsdl.cpp b/linden/indra/llwindow/llwindowsdl.cpp
new file mode 100644
index 0000000..a94284e
--- /dev/null
+++ b/linden/indra/llwindow/llwindowsdl.cpp
@@ -0,0 +1,2506 @@
1/**
2 * @file llwindowsdl.cpp
3 * @brief Platform-dependent implementation of llwindow
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#if LL_SDL
29
30#include "linden_common.h"
31
32#include "llwindowsdl.h"
33#include "llkeyboardsdl.h"
34#include "llerror.h"
35#include "llgl.h"
36#include "llstring.h"
37#include "lldir.h"
38
39#include "llglheaders.h"
40
41#include "indra_constants.h"
42
43#if LL_GTK
44# include "gtk/gtk.h"
45#endif // LL_GTK
46
47#if LL_LINUX
48// not necessarily available on random SDL platforms, so #if LL_LINUX
49// for execv(), waitpid(), fork()
50# include <unistd.h>
51# include <sys/types.h>
52# include <sys/wait.h>
53#endif // LL_LINUX
54
55extern BOOL gDebugWindowProc;
56
57// culled from winuser.h
58//const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
59// On the Mac, the scroll wheel reports a delta of 1 for each detent.
60// There's also acceleration for faster scrolling, based on a slider in the system preferences.
61const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */
62const S32 BITS_PER_PIXEL = 32;
63const S32 MAX_NUM_RESOLUTIONS = 32;
64
65//
66// LLWindowSDL
67//
68
69#if LL_X11
70# include <X11/Xutil.h>
71// A global! Well, SDL isn't really designed for communicating
72// with multiple physical X11 displays. Heck, it's not really
73// designed for multiple X11 windows.
74// So, we need this for the SDL/X11 event filter callback (which
75// doesnt have a userdata parameter) and more.
76static Display *SDL_Display = NULL;
77static Window SDL_XWindowID = None;
78#endif //LL_X11
79
80// TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for the same reasons)
81// For SDL, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
82// The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
83// require a pointer to the LLWindowMacSDL object. Stash it here and maintain in the constructor and destructor.
84// This assumes that there will be only one object of this class at any time. Hopefully this is true.
85static LLWindowSDL *gWindowImplementation = NULL;
86
87static BOOL was_fullscreen = FALSE;
88
89// Cross-platform bits:
90
91void show_window_creation_error(const char* title)
92{
93 llwarns << title << llendl;
94 shell_open( "help/window_creation_error.html");
95 /*
96 OSMessageBox(
97 "Second Life is unable to run because it can't set up your display.\n"
98 "We need to be able to make a 32-bit color window at 1024x768, with\n"
99 "an 8 bit alpha channel.\n"
100 "\n"
101 "First, be sure your monitor is set to True Color (32-bit) in\n"
102 "Start -> Control Panels -> Display -> Settings.\n"
103 "\n"
104 "Otherwise, this may be due to video card driver issues.\n"
105 "Please make sure you have the latest video card drivers installed.\n"
106 "ATI drivers are available at http://www.ati.com/\n"
107 "nVidia drivers are available at http://www.nvidia.com/\n"
108 "\n"
109 "If you continue to receive this message, contact customer service.",
110 title,
111 OSMB_OK);
112 */
113}
114
115
116#if LL_GTK
117// Check the runtime GTK version for goodness.
118static BOOL maybe_do_gtk_diagnostics(void)
119{
120 static BOOL done_gtk_diag = FALSE;
121 static BOOL is_good = TRUE;
122 gtk_disable_setlocale();
123 if ((!done_gtk_diag) && gtk_init_check(NULL, NULL))
124 {
125 llinfos << "GTK Initialized." << llendl;
126 llinfos << "- Compiled against GTK version "
127 << GTK_MAJOR_VERSION << "."
128 << GTK_MINOR_VERSION << "."
129 << GTK_MICRO_VERSION << llendl;
130 llinfos << "- Running against GTK version "
131 << gtk_major_version << "."
132 << gtk_minor_version << "."
133 << gtk_micro_version << llendl;
134 gchar *gtk_warning;
135 gtk_warning = gtk_check_version(GTK_MAJOR_VERSION,
136 GTK_MINOR_VERSION,
137 GTK_MICRO_VERSION);
138 if (gtk_warning)
139 {
140 llwarns << "- GTK COMPATIBILITY WARNING: " <<
141 gtk_warning << llendl;
142 is_good = FALSE;
143 }
144
145 done_gtk_diag = TRUE;
146 }
147 return is_good;
148}
149#endif // LL_GTK
150
151
152BOOL check_for_card(const char* RENDERER, const char* bad_card)
153{
154 if (!strncasecmp(RENDERER, bad_card, strlen(bad_card)))
155 {
156 char buffer[1024];
157 sprintf(buffer,
158 "Your video card appears to be a %s, which Second Life does not support.\n"
159 "\n"
160 "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
161 "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
162 "and ATI Radeon 8500 or better.\n"
163 "\n"
164 "If you own a supported card and continue to receive this message, try \n"
165 "updating to the latest video card drivers. Otherwise look in the\n"
166 "secondlife.com support section or e-mail technical support\n"
167 "\n"
168 "You can try to run Second Life, but it will probably crash or run\n"
169 "very slowly. Try anyway?",
170 bad_card);
171 S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
172 if (OSBTN_YES == button)
173 {
174 return FALSE;
175 }
176 else
177 {
178 return TRUE;
179 }
180 }
181
182 return FALSE;
183}
184
185
186
187
188LLWindowSDL::LLWindowSDL(char *title, S32 x, S32 y, S32 width,
189 S32 height, U32 flags,
190 BOOL fullscreen, BOOL clearBg,
191 BOOL disable_vsync, BOOL use_gl,
192 BOOL ignore_pixel_depth)
193 : LLWindow(fullscreen, flags), mGamma(1.0f)
194{
195 // Initialize the keyboard
196 gKeyboard = new LLKeyboardSDL();
197 // Note that we can't set up key-repeat until after SDL has init'd video
198
199 // Ignore use_gl for now, only used for drones on PC
200 mWindow = NULL;
201 mCursorDecoupled = FALSE;
202 mCursorLastEventDeltaX = 0;
203 mCursorLastEventDeltaY = 0;
204 mCursorIgnoreNextDelta = FALSE;
205 mNeedsResize = FALSE;
206 mOverrideAspectRatio = 0.f;
207 mGrabbyKeyFlags = 0;
208 mReallyCapturedCount = 0;
209 mHaveInputFocus = -1;
210 mIsMinimized = -1;
211
212 // Get the original aspect ratio of the main device.
213 mOriginalAspectRatio = 1024.0 / 768.0; // !!! FIXME //(double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
214
215 if (!title)
216 title = "SDL Window"; // *FIX: (???)
217
218 // Stash the window title
219 mWindowTitle = new char[strlen(title) + 1];
220 strcpy(mWindowTitle, title);
221
222 // Create the GL context and set it up for windowed or fullscreen, as appropriate.
223 if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
224 {
225 gGLManager.initGL();
226
227 //start with arrow cursor
228 initCursors();
229 setCursor( UI_CURSOR_ARROW );
230 }
231
232 stop_glerror();
233
234 // Stash an object pointer for OSMessageBox()
235 gWindowImplementation = this;
236
237#if LL_X11
238 mFlashing = FALSE;
239#endif // LL_X11
240}
241
242static SDL_Surface *Load_BMP_Resource(const char *basename)
243{
244 const int PATH_BUFFER_SIZE=1000;
245 char path_buffer[PATH_BUFFER_SIZE];
246
247 // Figure out where our BMP is living on the disk
248 snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",
249 gDirUtilp->getAppRODataDir().c_str(),
250 gDirUtilp->getDirDelimiter().c_str(),
251 gDirUtilp->getDirDelimiter().c_str(),
252 basename);
253 path_buffer[PATH_BUFFER_SIZE-1] = '\0';
254
255 return SDL_LoadBMP(path_buffer);
256}
257
258BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
259{
260 //bool glneedsinit = false;
261// const char *gllibname = null;
262
263 llinfos << "createContext, fullscreen=" << fullscreen <<
264 " size=" << width << "x" << height << llendl;
265
266 // captures don't survive contexts
267 mGrabbyKeyFlags = 0;
268 mReallyCapturedCount = 0;
269
270 if (SDL_Init(SDL_INIT_VIDEO) < 0)
271 {
272 llinfos << "sdl_init() failed! " << SDL_GetError() << llendl;
273 setupFailure("window creation error", "error", OSMB_OK);
274 return false;
275 }
276
277 SDL_version c_sdl_version;
278 SDL_VERSION(&c_sdl_version);
279 llinfos << "Compiled against SDL "
280 << int(c_sdl_version.major) << "."
281 << int(c_sdl_version.minor) << "."
282 << int(c_sdl_version.patch) << llendl;
283 const SDL_version *r_sdl_version;
284 r_sdl_version = SDL_Linked_Version();
285 llinfos << " Running against SDL "
286 << int(r_sdl_version->major) << "."
287 << int(r_sdl_version->minor) << "."
288 << int(r_sdl_version->patch) << llendl;
289
290 const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo( );
291 if (!videoInfo)
292 {
293 llinfos << "SDL_GetVideoInfo() failed! " << SDL_GetError() << llendl;
294 setupFailure("Window creation error", "Error", OSMB_OK);
295 return FALSE;
296 }
297
298 SDL_EnableUNICODE(1);
299 SDL_WM_SetCaption(mWindowTitle, mWindowTitle);
300
301 // Set the application icon.
302 SDL_Surface *bmpsurface;
303 bmpsurface = Load_BMP_Resource("ll_icon.BMP");
304 if (bmpsurface)
305 {
306 // This attempts to give a black-keyed mask to the icon.
307 SDL_SetColorKey(bmpsurface,
308 SDL_SRCCOLORKEY,
309 SDL_MapRGB(bmpsurface->format, 0,0,0) );
310 SDL_WM_SetIcon(bmpsurface, NULL);
311 // The SDL examples cheerfully avoid freeing the icon
312 // surface, but I'm betting that's leaky.
313 SDL_FreeSurface(bmpsurface);
314 bmpsurface = NULL;
315 }
316
317 // note: these SetAttributes make Tom's 9600-on-AMD64 fail to
318 // get a visual, but it's broken anyway when it does, and without
319 // these SetAttributes we might easily get an avoidable substandard
320 // visual to work with on most other machines.
321 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
322 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
323 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
324 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24);
325 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
326
327 // *FIX: try to toggle vsync here?
328
329 mFullscreen = fullscreen;
330 was_fullscreen = fullscreen;
331
332 int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
333
334 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
335
336 mSDLFlags = sdlflags;
337
338 if (mFullscreen)
339 {
340 llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
341
342 // If the requested width or height is 0, find the best default for the monitor.
343 if((width == 0) || (height == 0))
344 {
345 // Scan through the list of modes, looking for one which has:
346 // height between 700 and 800
347 // aspect ratio closest to the user's original mode
348 S32 resolutionCount = 0;
349 LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
350
351 if(resolutionList != NULL)
352 {
353 F32 closestAspect = 0;
354 U32 closestHeight = 0;
355 U32 closestWidth = 0;
356 int i;
357
358 llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
359
360 for(i=0; i < resolutionCount; i++)
361 {
362 F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
363
364 llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
365
366 if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
367 (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
368 {
369 llinfos << " (new closest mode) " << llendl;
370
371 // This is the closest mode we've seen yet.
372 closestWidth = resolutionList[i].mWidth;
373 closestHeight = resolutionList[i].mHeight;
374 closestAspect = aspect;
375 }
376 }
377
378 width = closestWidth;
379 height = closestHeight;
380 }
381 }
382
383 if((width == 0) || (height == 0))
384 {
385 // Mode search failed for some reason. Use the old-school default.
386 width = 1024;
387 height = 768;
388 }
389
390 mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
391
392 if (mWindow)
393 {
394 mFullscreen = TRUE;
395 was_fullscreen = TRUE;
396 mFullscreenWidth = mWindow->w;
397 mFullscreenHeight = mWindow->h;
398 mFullscreenBits = mWindow->format->BitsPerPixel;
399 mFullscreenRefresh = -1;
400
401 llinfos << "Running at " << mFullscreenWidth
402 << "x" << mFullscreenHeight
403 << "x" << mFullscreenBits
404 << " @ " << mFullscreenRefresh
405 << llendl;
406 }
407 else
408 {
409 llwarns << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << llendl;
410 // No fullscreen support
411 mFullscreen = FALSE;
412 was_fullscreen = FALSE;
413 mFullscreenWidth = -1;
414 mFullscreenHeight = -1;
415 mFullscreenBits = -1;
416 mFullscreenRefresh = -1;
417
418 char error[256];
419 sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
420 OSMessageBox(error, "Error", OSMB_OK);
421 }
422 }
423
424 if(!mFullscreen && (mWindow == NULL))
425 {
426 if (width == 0)
427 width = 1024;
428 if (height == 0)
429 width = 768;
430
431 llinfos << "createContext: creating window " << width << "x" << height << "x" << bits << llendl;
432 mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
433
434 if (!mWindow)
435 {
436 llwarns << "createContext: window creation failure. SDL: " << SDL_GetError() << llendl;
437 setupFailure("Window creation error", "Error", OSMB_OK);
438 return FALSE;
439 }
440 } else if (!mFullscreen && (mWindow != NULL))
441 {
442 llinfos << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << llendl;
443 }
444
445 /*if (!load_all_glsyms(gllibname))
446 {
447 SDL_QuitSubSystem(SDL_INIT_VIDEO);
448 return FALSE;
449 }*/
450
451 gGLManager.mVRAM = videoInfo->video_mem / 1024;
452 if (gGLManager.mVRAM != 0)
453 {
454 llinfos << "Detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
455 }
456 // If VRAM is not detected, that is handled later
457
458#if 0 // *FIX: all video cards suck under Linux. :)
459 // Since we just created the context, it needs to be set up.
460 glNeedsInit = TRUE;
461 if(glNeedsInit)
462 {
463 // Check for some explicitly unsupported cards.
464 const char* RENDERER = (const char*) glGetString(GL_RENDERER);
465
466 const char* CARD_LIST[] =
467 { "RAGE 128",
468 "RIVA TNT2",
469 "Intel 810",
470 "3Dfx/Voodoo3",
471 "Radeon 7000",
472 "Radeon 7200",
473 "Radeon 7500",
474 "Radeon DDR",
475 "Radeon VE",
476 "GDI Generic" };
477 const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
478
479 // Future candidates:
480 // ProSavage/Twister
481 // SuperSavage
482
483 S32 i;
484 for (i = 0; i < CARD_COUNT; i++)
485 {
486 if (check_for_card(RENDERER, CARD_LIST[i]))
487 {
488 close();
489 shell_open( "help/unsupported_card.html" );
490 return FALSE;
491 }
492 }
493 }
494#endif
495
496 GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits;
497
498 glGetIntegerv(GL_RED_BITS, &redBits);
499 glGetIntegerv(GL_GREEN_BITS, &greenBits);
500 glGetIntegerv(GL_BLUE_BITS, &blueBits);
501 glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
502 glGetIntegerv(GL_DEPTH_BITS, &depthBits);
503 glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
504
505 llinfos << "GL buffer:" << llendl
506 llinfos << " Red Bits " << S32(redBits) << llendl
507 llinfos << " Green Bits " << S32(greenBits) << llendl
508 llinfos << " Blue Bits " << S32(blueBits) << llendl
509 llinfos << " Alpha Bits " << S32(alphaBits) << llendl
510 llinfos << " Depth Bits " << S32(depthBits) << llendl
511 llinfos << " Stencil Bits " << S32(stencilBits) << llendl;
512
513 GLint colorBits = redBits + greenBits + blueBits + alphaBits;
514 // fixme: actually, it's REALLY important for picking that we get at
515 // least 8 bits each of red,green,blue. Alpha we can be a bit more
516 // relaxed about if we have to.
517 if (colorBits < 32)
518 {
519 close();
520 setupFailure(
521 "Second Life requires True Color (32-bit) to run in a window.\n"
522 "Please go to Control Panels -> Display -> Settings and\n"
523 "set the screen to 32-bit color.\n"
524 "Alternately, if you choose to run fullscreen, Second Life\n"
525 "will automatically adjust the screen each time it runs.",
526 "Error",
527 OSMB_OK);
528 return FALSE;
529 }
530
531#if 0 // *FIX: we're going to brave it for now...
532 if (alphaBits < 8)
533 {
534 close();
535 setupFailure(
536 "Second Life is unable to run because it can't get an 8 bit alpha\n"
537 "channel. Usually this is due to video card driver issues.\n"
538 "Please make sure you have the latest video card drivers installed.\n"
539 "Also be sure your monitor is set to True Color (32-bit) in\n"
540 "Control Panels -> Display -> Settings.\n"
541 "If you continue to receive this message, contact customer service.",
542 "Error",
543 OSMB_OK);
544 return FALSE;
545 }
546#endif
547
548#if LL_X11
549 init_x11clipboard();
550#endif // LL_X11
551
552 // We need to do this here, once video is init'd
553 if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
554 SDL_DEFAULT_REPEAT_INTERVAL))
555 llwarns << "Couldn't enable key-repeat: " << SDL_GetError() <<llendl;
556
557 // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
558 return TRUE;
559}
560
561
562// changing fullscreen resolution, or switching between windowed and fullscreen mode.
563BOOL LLWindowSDL::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
564{
565 const BOOL needsRebuild = TRUE; // Just nuke the context and start over.
566 BOOL result = true;
567
568 llinfos << "switchContext, fullscreen=" << fullscreen << llendl;
569 stop_glerror();
570 if(needsRebuild)
571 {
572 destroyContext();
573 result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
574 if (result)
575 {
576 gGLManager.initGL();
577
578 //start with arrow cursor
579 initCursors();
580 setCursor( UI_CURSOR_ARROW );
581 }
582 }
583
584 stop_glerror();
585
586 return result;
587}
588
589void LLWindowSDL::destroyContext()
590{
591 llinfos << "destroyContext begins" << llendl;
592#if LL_X11
593 quit_x11clipboard();
594#endif // LL_X11
595
596 // Clean up remaining GL state before blowing away window
597 llinfos << "shutdownGL begins" << llendl;
598 gGLManager.shutdownGL();
599 llinfos << "SDL_QuitSS/VID begins" << llendl;
600 SDL_QuitSubSystem(SDL_INIT_VIDEO); // *FIX: this might be risky...
601 //unload_all_glsyms();
602
603 mWindow = NULL;
604}
605
606LLWindowSDL::~LLWindowSDL()
607{
608 quitCursors();
609 destroyContext();
610
611 if(mSupportedResolutions != NULL)
612 {
613 delete []mSupportedResolutions;
614 }
615
616 delete[] mWindowTitle;
617
618 gWindowImplementation = NULL;
619}
620
621
622void LLWindowSDL::show()
623{
624 // *FIX: What to do with SDL?
625}
626
627void LLWindowSDL::hide()
628{
629 // *FIX: What to do with SDL?
630}
631
632void LLWindowSDL::minimize()
633{
634 // *FIX: What to do with SDL?
635}
636
637void LLWindowSDL::restore()
638{
639 // *FIX: What to do with SDL?
640}
641
642
643// close() destroys all OS-specific code associated with a window.
644// Usually called from LLWindowManager::destroyWindow()
645void LLWindowSDL::close()
646{
647 // Is window is already closed?
648 // if (!mWindow)
649 // {
650 // return;
651 // }
652
653 // Make sure cursor is visible and we haven't mangled the clipping state.
654 setMouseClipping(FALSE);
655 showCursor();
656
657 destroyContext();
658}
659
660BOOL LLWindowSDL::isValid()
661{
662 return (mWindow != NULL);
663}
664
665BOOL LLWindowSDL::getVisible()
666{
667 BOOL result = FALSE;
668
669 // *FIX: This isn't really right...
670 // Then what is?
671 if (mWindow)
672 {
673 result = TRUE;
674 }
675
676 return(result);
677}
678
679BOOL LLWindowSDL::getMinimized()
680{
681 BOOL result = FALSE;
682
683 if (mWindow && (1 == mIsMinimized))
684 {
685 result = TRUE;
686 }
687 return(result);
688}
689
690BOOL LLWindowSDL::getMaximized()
691{
692 BOOL result = FALSE;
693
694 if (mWindow)
695 {
696 // TODO
697 }
698
699 return(result);
700}
701
702BOOL LLWindowSDL::maximize()
703{
704 // TODO
705 return FALSE;
706}
707
708BOOL LLWindowSDL::getFullscreen()
709{
710 return mFullscreen;
711}
712
713BOOL LLWindowSDL::getPosition(LLCoordScreen *position)
714{
715 // *FIX: can anything be done with this?
716 position->mX = 0;
717 position->mY = 0;
718 return TRUE;
719}
720
721BOOL LLWindowSDL::getSize(LLCoordScreen *size)
722{
723 if (mWindow)
724 {
725 size->mX = mWindow->w;
726 size->mY = mWindow->h;
727 return (TRUE);
728 }
729
730 llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
731 return (FALSE);
732}
733
734BOOL LLWindowSDL::getSize(LLCoordWindow *size)
735{
736 if (mWindow)
737 {
738 size->mX = mWindow->w;
739 size->mY = mWindow->h;
740 return (TRUE);
741 }
742
743 llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
744 return (FALSE);
745}
746
747BOOL LLWindowSDL::setPosition(const LLCoordScreen position)
748{
749 if(mWindow)
750 {
751 // *FIX: (???)
752 //MacMoveWindow(mWindow, position.mX, position.mY, false);
753 }
754
755 return TRUE;
756}
757
758BOOL LLWindowSDL::setSize(const LLCoordScreen size)
759{
760 if(mWindow)
761 {
762 // *FIX: (???)
763 //SizeWindow(mWindow, size.mX, size.mY, true);
764 }
765
766 return TRUE;
767}
768
769void LLWindowSDL::swapBuffers()
770{
771 if (mWindow)
772 SDL_GL_SwapBuffers();
773}
774
775F32 LLWindowSDL::getGamma()
776{
777 return 1/mGamma;
778}
779
780BOOL LLWindowSDL::restoreGamma()
781{
782 //CGDisplayRestoreColorSyncSettings();
783 SDL_SetGamma(1.0f, 1.0f, 1.0f);
784 return true;
785}
786
787BOOL LLWindowSDL::setGamma(const F32 gamma)
788{
789 mGamma = gamma;
790 if (mGamma == 0) mGamma = 0.1f;
791 mGamma = 1/mGamma;
792 SDL_SetGamma(mGamma, mGamma, mGamma);
793 return true;
794}
795
796BOOL LLWindowSDL::isCursorHidden()
797{
798 return mCursorHidden;
799}
800
801
802
803// Constrains the mouse to the window.
804void LLWindowSDL::setMouseClipping( BOOL b )
805{
806 //llinfos << "LLWindowSDL::setMouseClipping " << b << llendl;
807 // Just stash the requested state. We'll simulate this when the cursor is hidden by decoupling.
808 mIsMouseClipping = b;
809 //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
810 adjustCursorDecouple();
811}
812
813BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position)
814{
815 BOOL result = TRUE;
816 LLCoordScreen screen_pos;
817
818 if (!convertCoords(position, &screen_pos))
819 {
820 return FALSE;
821 }
822
823 //llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
824
825 SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
826
827 // Under certain circumstances, this will trigger us to decouple the cursor.
828 adjustCursorDecouple(true);
829
830 return result;
831}
832
833BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position)
834{
835 //Point cursor_point;
836 LLCoordScreen screen_pos;
837
838 //GetMouse(&cursor_point);
839 int x, y;
840 SDL_GetMouseState(&x, &y);
841
842 screen_pos.mX = x;
843 screen_pos.mY = y;
844
845 return convertCoords(screen_pos, position);
846}
847
848void LLWindowSDL::adjustCursorDecouple(bool warpingMouse)
849{
850 if(mIsMouseClipping && mCursorHidden)
851 {
852 if(warpingMouse)
853 {
854 // The cursor should be decoupled. Make sure it is.
855 if(!mCursorDecoupled)
856 {
857 // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
858 //CGAssociateMouseAndMouseCursorPosition(false);
859 mCursorDecoupled = true;
860 mCursorIgnoreNextDelta = TRUE;
861 }
862 }
863 }
864 else
865 {
866 // The cursor should not be decoupled. Make sure it isn't.
867 if(mCursorDecoupled)
868 {
869 // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
870 //CGAssociateMouseAndMouseCursorPosition(true);
871 mCursorDecoupled = false;
872 }
873 }
874}
875
876F32 LLWindowSDL::getNativeAspectRatio()
877{
878#if 0
879 // RN: this hack presumes that the largest supported resolution is monitor-limited
880 // and that pixels in that mode are square, therefore defining the native aspect ratio
881 // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
882 S32 num_resolutions;
883 LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
884
885
886 return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
887 //rn: AC
888#endif
889
890 // MBW -- there are a couple of bad assumptions here. One is that the display list won't include
891 // ridiculous resolutions nobody would ever use. The other is that the list is in order.
892
893 // New assumptions:
894 // - pixels are square (the only reasonable choice, really)
895 // - The user runs their display at a native resolution, so the resolution of the display
896 // when the app is launched has an aspect ratio that matches the monitor.
897
898 //RN: actually, the assumption that there are no ridiculous resolutions (above the display's native capabilities) has
899 // been born out in my experience.
900 // Pixels are often not square (just ask the people who run their LCDs at 1024x768 or 800x600 when running fullscreen, like me)
901 // The ordering of display list is a blind assumption though, so we should check for max values
902 // Things might be different on the Mac though, so I'll defer to MBW
903
904 // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
905 // switching, and stashes it in mOriginalAspectRatio. Here, we just return it.
906
907 if (mOverrideAspectRatio > 0.f)
908 {
909 return mOverrideAspectRatio;
910 }
911
912 return mOriginalAspectRatio;
913}
914
915F32 LLWindowSDL::getPixelAspectRatio()
916{
917 F32 pixel_aspect = 1.f;
918 if (getFullscreen())
919 {
920 LLCoordScreen screen_size;
921 getSize(&screen_size);
922 pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
923 }
924
925 return pixel_aspect;
926}
927
928
929// some of this stuff is to support 'temporarily windowed' mode so that
930// dialogs are still usable in fullscreen. HOWEVER! - it's not enabled/working
931// yet.
932static LLCoordScreen old_size;
933static BOOL old_fullscreen;
934void LLWindowSDL::beforeDialog()
935{
936 llinfos << "LLWindowSDL::beforeDialog()" << llendl;
937
938 if (SDLReallyCaptureInput(FALSE) // must ungrab input so popup works!
939 && getSize(&old_size))
940 {
941 old_fullscreen = was_fullscreen;
942
943 if (old_fullscreen)
944 {
945 // NOT YET WORKING
946 //switchContext(FALSE, old_size, TRUE);
947 }
948 }
949
950#if LL_X11
951 if (SDL_Display)
952 {
953 // Everything that we/SDL asked for should happen before we
954 // potentially hand control over to GTK.
955 XSync(SDL_Display, False);
956 }
957#endif // LL_X11
958
959#if LL_GTK
960 // this is a good time to grab some GTK version information for
961 // diagnostics
962 maybe_do_gtk_diagnostics();
963#endif // LL_GTK
964}
965
966void LLWindowSDL::afterDialog()
967{
968 llinfos << "LLWindowSDL::afterDialog()" << llendl;
969 if (old_fullscreen && !was_fullscreen)
970 {
971 // *FIX: NOT YET WORKING (see below)
972 //switchContext(TRUE, old_size, TRUE);
973 }
974 // *FIX: we need to restore the GL context using
975 // LLViewerWindow::restoreGL() - but how??
976}
977
978
979S32 LLWindowSDL::stat(const char* file_name, struct stat* stat_info)
980{
981 return ::stat( file_name, stat_info );
982}
983
984#if LL_X11
985// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
986void LLWindowSDL::x11_set_urgent(BOOL urgent)
987{
988 if (SDL_Display && !mFullscreen)
989 {
990 XWMHints *wm_hints;
991
992 llinfos << "X11 hint for urgency, " << urgent << llendl;
993
994 wm_hints = XGetWMHints(SDL_Display, mSDL_XWindowID);
995 if (!wm_hints)
996 wm_hints = XAllocWMHints();
997
998 if (urgent)
999 wm_hints->flags |= XUrgencyHint;
1000 else
1001 wm_hints->flags &= ~XUrgencyHint;
1002
1003 XSetWMHints(SDL_Display, mSDL_XWindowID, wm_hints);
1004 XFree(wm_hints);
1005 XSync(SDL_Display, False);
1006 }
1007}
1008#endif // LL_X11
1009
1010void LLWindowSDL::flashIcon(F32 seconds)
1011{
1012#if !LL_X11
1013 llinfos << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
1014#else
1015 llinfos << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
1016
1017 F32 remaining_time = mFlashTimer.getRemainingTimeF32();
1018 if (remaining_time < seconds)
1019 remaining_time = seconds;
1020 mFlashTimer.reset();
1021 mFlashTimer.setTimerExpirySec(remaining_time);
1022
1023 x11_set_urgent(TRUE);
1024 mFlashing = TRUE;
1025#endif // LL_X11
1026}
1027
1028#if LL_X11
1029/* Lots of low-level X11 stuff to handle X11 copy-and-paste */
1030
1031/* Our X11 clipboard support is a bit bizarre in various
1032 organically-grown ways. Ideally it should be fixed to do
1033 real string-type negotiation (this would make pasting to
1034 xterm faster and pasting to UTF-8 emacs work properly), but
1035 right now it has the rare and desirable trait of being
1036 generally stable and working. */
1037
1038/* PRIMARY and CLIPBOARD are the two main kinds of
1039 X11 clipboard. A third are the CUT_BUFFERs which an
1040 obsolete holdover from X10 days and use a quite orthogonal
1041 mechanism. CLIPBOARD is the type whose design most
1042 closely matches SL's own win32-alike explicit copy-and-paste
1043 paradigm.
1044
1045 Pragmatically we support all three to varying degrees. When
1046 we paste into SL, it is strictly from CLIPBOARD. When we copy,
1047 we support (to as full an extent as the clipboard content type
1048 allows) CLIPBOARD, PRIMARY, and CUT_BUFFER0.
1049 */
1050#define SL_READWRITE_XCLIPBOARD_TYPE XInternAtom(SDL_Display, "CLIPBOARD", False)
1051#define SL_WRITE_XCLIPBOARD_TYPE XA_PRIMARY
1052
1053/* This is where our own private cutbuffer goes - we don't use
1054 a regular cutbuffer (XA_CUT_BUFFER0 etc) for intermediate
1055 storage because their use isn't really defined for holding UTF8. */
1056#define SL_CUTBUFFER_TYPE XInternAtom(SDL_Display, "SECONDLIFE_CUTBUFFER", False)
1057
1058/* These defines, and convert_data/convert_x11clipboard,
1059 mostly exist to support non-text or unusually-encoded
1060 clipboard data, which we don't really have a need for at
1061 the moment. */
1062#define SDLCLIPTYPE(A, B, C, D) (int)((A<<24)|(B<<16)|(C<<8)|(D<<0))
1063#define FORMAT_PREFIX "SECONDLIFE_x11clipboard_0x"
1064
1065typedef Atom x11clipboard_type;
1066
1067static
1068x11clipboard_type convert_format(int type)
1069{
1070 switch (type)
1071 {
1072 case SDLCLIPTYPE('T', 'E', 'X', 'T'):
1073 // old-style X11 clipboard, strictly only ISO 8859-1 encoding
1074 return XA_STRING;
1075 case SDLCLIPTYPE('U', 'T', 'F', '8'):
1076 // newer de-facto UTF8 clipboard atom
1077 return XInternAtom(SDL_Display, "UTF8_STRING", False);
1078 default:
1079 {
1080 /* completely arbitrary clipboard types... we don't actually use
1081 these right now, and support is skeletal. */
1082 char format[sizeof(FORMAT_PREFIX)+8+1];
1083
1084 sprintf(format, "%s%08lx", FORMAT_PREFIX, (unsigned long)type);
1085 return XInternAtom(SDL_Display, format, False);
1086 }
1087 }
1088}
1089
1090/* convert platform string to x11 clipboard format. for our
1091 purposes this is pretty trivial right now. */
1092static int
1093convert_data(int type, char *dst, const char *src, int srclen)
1094{
1095 int dstlen;
1096
1097 dstlen = 0;
1098 switch (type)
1099 {
1100 case SDLCLIPTYPE('T', 'E', 'X', 'T'):
1101 case SDLCLIPTYPE('U', 'T', 'F', '8'):
1102 if ( srclen == 0 )
1103 srclen = strlen(src);
1104
1105 dstlen = srclen + 1;
1106
1107 if ( dst ) // assume caller made it big enough by asking us
1108 {
1109 memcpy(dst, src, srclen);
1110 dst[srclen] = '\0';
1111 }
1112 break;
1113
1114 default:
1115 llwarns << "convert_data: Unknown medium type" << llendl;
1116 break;
1117 }
1118 return(dstlen);
1119}
1120
1121/* Convert x11clipboard data to platform string. This too is
1122 pretty trivial for our needs right now, and just about identical
1123 to above. */
1124static int
1125convert_x11clipboard(int type, char *dst, const char *src, int srclen)
1126{
1127 int dstlen;
1128
1129 dstlen = 0;
1130 switch (type)
1131 {
1132 case SDLCLIPTYPE('U', 'T', 'F', '8'):
1133 case SDLCLIPTYPE('T', 'E', 'X', 'T'):
1134 if ( srclen == 0 )
1135 srclen = strlen(src);
1136
1137 dstlen = srclen + 1;
1138
1139 if ( dst ) // assume caller made it big enough by asking us
1140 {
1141 memcpy(dst, src, srclen);
1142 dst[srclen] = '\0';
1143 }
1144 break;
1145
1146 default:
1147 llwarns << "convert_x11clipboard: Unknown medium type" << llendl;
1148 break;
1149 }
1150 return dstlen;
1151}
1152
1153int
1154LLWindowSDL::is_empty_x11clipboard(void)
1155{
1156 int retval;
1157
1158 Lock_Display();
1159 retval = ( XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE) == None );
1160 Unlock_Display();
1161
1162 return(retval);
1163}
1164
1165void
1166LLWindowSDL::put_x11clipboard(int type, int srclen, const char *src)
1167{
1168 x11clipboard_type format;
1169 int dstlen;
1170 char *dst;
1171
1172 format = convert_format(type);
1173 dstlen = convert_data(type, NULL, src, srclen);
1174
1175 dst = (char *)malloc(dstlen);
1176 if ( dst != NULL )
1177 {
1178 Window root = DefaultRootWindow(SDL_Display);
1179 Lock_Display();
1180 convert_data(type, dst, src, srclen);
1181 // Cutbuffers are only allowed to have STRING atom types,
1182 // but Emacs puts UTF8 inside them anyway. We cautiously
1183 // don't.
1184 if (type == SDLCLIPTYPE('T','E','X','T'))
1185 {
1186 // dstlen-1 so we don't include the trailing \0
1187 llinfos << "X11: Populating cutbuffer." <<llendl;
1188 XChangeProperty(SDL_Display, root,
1189 XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace,
1190 (unsigned char*)dst, dstlen-1);
1191 } else {
1192 // Should we clear the cutbuffer if we can't put the selection in
1193 // it because it's a UTF8 selection? Eh, no great reason I think.
1194 //XDeleteProperty(SDL_Display, root, XA_CUT_BUFFER0);
1195 }
1196 // Private cutbuffer of an appropriate type.
1197 XChangeProperty(SDL_Display, root,
1198 SL_CUTBUFFER_TYPE, format, 8, PropModeReplace,
1199 (unsigned char*)dst, dstlen-1);
1200 free(dst);
1201
1202 /* Claim ownership of both PRIMARY and CLIPBOARD */
1203 XSetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE,
1204 mSDL_XWindowID, CurrentTime);
1205 XSetSelectionOwner(SDL_Display, SL_WRITE_XCLIPBOARD_TYPE,
1206 mSDL_XWindowID, CurrentTime);
1207
1208 Unlock_Display();
1209 }
1210}
1211
1212void
1213LLWindowSDL::get_x11clipboard(int type, int *dstlen, char **dst)
1214{
1215 x11clipboard_type format;
1216
1217 *dstlen = 0;
1218 format = convert_format(type);
1219
1220 Window owner;
1221 Atom selection;
1222 Atom seln_type;
1223 int seln_format;
1224 unsigned long nbytes;
1225 unsigned long overflow;
1226 char *src;
1227
1228 Lock_Display();
1229 owner = XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE);
1230 Unlock_Display();
1231 if (owner == None)
1232 {
1233 // Fall right back to ancient X10 cut-buffers
1234 owner = DefaultRootWindow(SDL_Display);
1235 selection = XA_CUT_BUFFER0;
1236 } else if (owner == mSDL_XWindowID)
1237 {
1238 // Use our own uncooked opaque string property
1239 owner = DefaultRootWindow(SDL_Display);
1240 selection = SL_CUTBUFFER_TYPE;
1241 }
1242 else
1243 {
1244 // Use full-on X11-style clipboard negotiation with the owning app
1245 int selection_response = 0;
1246 SDL_Event event;
1247
1248 owner = mSDL_XWindowID;
1249 Lock_Display();
1250 selection = XInternAtom(SDL_Display, "SDL_SELECTION", False);
1251 XConvertSelection(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE, format,
1252 selection, owner, CurrentTime);
1253 Unlock_Display();
1254 llinfos << "X11: Waiting for clipboard to arrive." <<llendl;
1255 while ( ! selection_response )
1256 {
1257 // Only look for SYSWMEVENTs, or we may lose keypresses
1258 // etc.
1259 SDL_PumpEvents();
1260 if (1 == SDL_PeepEvents(&event, 1, SDL_GETEVENT,
1261 SDL_SYSWMEVENTMASK) )
1262 {
1263 if ( event.type == SDL_SYSWMEVENT )
1264 {
1265 XEvent xevent =
1266 event.syswm.msg->event.xevent;
1267
1268 if ( (xevent.type == SelectionNotify)&&
1269 (xevent.xselection.requestor == owner) )
1270 selection_response = 1;
1271 }
1272 } else {
1273 llinfos << "X11: Waiting for SYSWM event..." << llendl;
1274 }
1275 }
1276 llinfos << "X11: Clipboard arrived." <<llendl;
1277 }
1278
1279 Lock_Display();
1280 if ( XGetWindowProperty(SDL_Display, owner, selection, 0, INT_MAX/4,
1281 False, format, &seln_type, &seln_format,
1282 &nbytes, &overflow, (unsigned char **)&src) == Success )
1283 {
1284 if ( seln_type == format )
1285 {
1286 *dstlen = convert_x11clipboard(type, NULL, src, nbytes);
1287 *dst = (char *)realloc(*dst, *dstlen);
1288 if ( *dst == NULL )
1289 *dstlen = 0;
1290 else
1291 convert_x11clipboard(type, *dst, src, nbytes);
1292 }
1293 XFree(src);
1294 }
1295
1296 Unlock_Display();
1297}
1298
1299int clipboard_filter_callback(const SDL_Event *event)
1300{
1301 /* Post all non-window manager specific events */
1302 if ( event->type != SDL_SYSWMEVENT )
1303 {
1304 return(1);
1305 }
1306
1307 /* Handle window-manager specific clipboard events */
1308 switch (event->syswm.msg->event.xevent.type) {
1309 /* Copy the selection from SL_CUTBUFFER_TYPE to the requested property */
1310 case SelectionRequest: {
1311 XSelectionRequestEvent *req;
1312 XEvent sevent;
1313 int seln_format;
1314 unsigned long nbytes;
1315 unsigned long overflow;
1316 unsigned char *seln_data;
1317
1318 req = &event->syswm.msg->event.xevent.xselectionrequest;
1319 sevent.xselection.type = SelectionNotify;
1320 sevent.xselection.display = req->display;
1321 sevent.xselection.selection = req->selection;
1322 sevent.xselection.target = None;
1323 sevent.xselection.property = None;
1324 sevent.xselection.requestor = req->requestor;
1325 sevent.xselection.time = req->time;
1326 if ( XGetWindowProperty(SDL_Display, DefaultRootWindow(SDL_Display),
1327 SL_CUTBUFFER_TYPE, 0, INT_MAX/4, False, req->target,
1328 &sevent.xselection.target, &seln_format,
1329 &nbytes, &overflow, &seln_data) == Success )
1330 {
1331 if ( sevent.xselection.target == req->target)
1332 {
1333 if ( sevent.xselection.target == XA_STRING ||
1334 sevent.xselection.target ==
1335 convert_format(SDLCLIPTYPE('U','T','F','8')) )
1336 {
1337 if ( seln_data[nbytes-1] == '\0' )
1338 --nbytes;
1339 }
1340 XChangeProperty(SDL_Display, req->requestor, req->property,
1341 req->target, seln_format, PropModeReplace,
1342 seln_data, nbytes);
1343 sevent.xselection.property = req->property;
1344#define XA_TARGETS XInternAtom(SDL_Display, "TARGETS", False)
1345 } else if (XA_TARGETS == req->target) {
1346 /* only advertise what we currently support */
1347 const int num_supported = 3;
1348 Atom supported[num_supported] = {
1349 XA_STRING, // will be over-written below
1350 XInternAtom(SDL_Display, "TEXT",False),
1351 XA_TARGETS
1352 };
1353 supported[0] = sevent.xselection.target;
1354 XChangeProperty(SDL_Display, req->requestor,
1355 req->property, XA_ATOM, 32, PropModeReplace,
1356 (unsigned char*)supported,
1357 num_supported);
1358 sevent.xselection.property = req->property;
1359 llinfos << "Clipboard: An app asked us what selections format we offer." << llendl;
1360 } else {
1361 llinfos << "Clipboard: An app requested an unsupported selection format " << req->target << ", we have " << sevent.xselection.target << llendl;
1362 sevent.xselection.target = None;
1363 }
1364 XFree(seln_data);
1365 }
1366 int sendret =
1367 XSendEvent(SDL_Display,req->requestor,False,0,&sevent);
1368 if ((sendret==BadValue) || (sendret==BadWindow))
1369 llwarns << "Clipboard SendEvent failed" << llendl;
1370 XSync(SDL_Display, False);
1371 }
1372 break;
1373 }
1374
1375 /* Post the event for X11 clipboard reading above */
1376 return(1);
1377}
1378
1379int
1380LLWindowSDL::init_x11clipboard(void)
1381{
1382 SDL_SysWMinfo info;
1383 int retval;
1384
1385 /* Grab the window manager specific information */
1386 retval = -1;
1387 SDL_SetError("SDL is not running on known window manager");
1388
1389 SDL_VERSION(&info.version);
1390 if ( SDL_GetWMInfo(&info) )
1391 {
1392 /* Save the information for later use */
1393 if ( info.subsystem == SDL_SYSWM_X11 )
1394 {
1395 SDL_Display = info.info.x11.display;
1396 SDL_XWindowID = info.info.x11.wmwindow;
1397 mSDL_XWindowID = info.info.x11.wmwindow;
1398 Lock_Display = info.info.x11.lock_func;
1399 Unlock_Display = info.info.x11.unlock_func;
1400
1401 /* Enable the special window hook events */
1402 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
1403 SDL_SetEventFilter(clipboard_filter_callback);
1404
1405 retval = 0;
1406 }
1407 else
1408 {
1409 SDL_SetError("SDL is not running on X11");
1410 }
1411 }
1412 return(retval);
1413}
1414
1415void
1416LLWindowSDL::quit_x11clipboard(void)
1417{
1418 SDL_Display = NULL;
1419 SDL_XWindowID = None;
1420 mSDL_XWindowID = None;
1421 Lock_Display = NULL;
1422 Unlock_Display = NULL;
1423
1424 SDL_SetEventFilter(NULL); // Stop custom event filtering
1425}
1426
1427/************************************************/
1428
1429BOOL LLWindowSDL::isClipboardTextAvailable()
1430{
1431 return !is_empty_x11clipboard();
1432}
1433
1434BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
1435{
1436 int cliplen; // seems 1 or 2 bytes longer than expected
1437 char *cliptext = NULL;
1438 get_x11clipboard(SDLCLIPTYPE('U','T','F','8'), &cliplen, &cliptext);
1439 if (cliptext)
1440 {
1441 llinfos << "X11: Got UTF8 clipboard text." << llendl;
1442 // at some future time we can use cliplen instead of relying on \0,
1443 // if we ever grok non-ascii, non-utf8 encodings on the clipboard.
1444 std::string clip_str(cliptext);
1445 // we can't necessarily trust the incoming text to be valid UTF-8,
1446 // but utf8str_to_wstring() seems to do an appropriate level of
1447 // validation for avoiding over-reads.
1448 dst = utf8str_to_wstring(clip_str);
1449 /*llinfos << "X11 pasteTextFromClipboard: cliplen=" << cliplen <<
1450 " strlen(cliptext)=" << strlen(cliptext) <<
1451 " clip_str.length()=" << clip_str.length() <<
1452 " dst.length()=" << dst.length() <<
1453 llendl;*/
1454 free(cliptext);
1455 return TRUE; // success
1456 }
1457 get_x11clipboard(SDLCLIPTYPE('T','E','X','T'), &cliplen, &cliptext);
1458 if (cliptext)
1459 {
1460 llinfos << "X11: Got ISO 8859-1 clipboard text." << llendl;
1461 std::string clip_str(cliptext);
1462 std::string utf8_str = rawstr_to_utf8(clip_str);
1463 dst = utf8str_to_wstring(utf8_str);
1464 free(cliptext);
1465 }
1466 return FALSE; // failure
1467}
1468
1469BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
1470{
1471 std::string utf8text = wstring_to_utf8str(s);
1472 const char* cstr = utf8text.c_str();
1473 int cstrlen = strlen(cstr);
1474 int i;
1475 for (i=0; i<cstrlen; ++i)
1476 {
1477 if (0x80 & (unsigned char)cstr[i])
1478 {
1479 // Found an 8-bit character; use new-style UTF8 clipboard
1480 llinfos << "X11: UTF8 copyTextToClipboard" << llendl;
1481 put_x11clipboard(SDLCLIPTYPE('U','T','F','8'), cstrlen, cstr);
1482 return TRUE;
1483 }
1484 }
1485 // Didn't find any 8-bit characters; use old-style ISO 8859-1 clipboard
1486 llinfos << "X11: ISO 8859-1 copyTextToClipboard" << llendl;
1487 put_x11clipboard(SDLCLIPTYPE('T','E','X','T'), cstrlen, cstr);
1488 return TRUE;
1489}
1490#else
1491
1492BOOL LLWindowSDL::isClipboardTextAvailable()
1493{
1494 return FALSE; // unsupported
1495}
1496
1497BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
1498{
1499 return FALSE; // unsupported
1500}
1501
1502BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
1503{
1504 return FALSE; // unsupported
1505}
1506#endif // LL_X11
1507
1508BOOL LLWindowSDL::sendEmail(const char* address, const char* subject, const char* body_text,
1509 const char* attachment, const char* attachment_displayed_name )
1510{
1511 // MBW -- XXX -- Um... yeah. I'll get to this later.
1512
1513 return FALSE;
1514}
1515
1516
1517LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
1518{
1519 if (!mSupportedResolutions)
1520 {
1521 mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
1522 mNumSupportedResolutions = 0;
1523
1524 SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
1525 if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
1526 {
1527 int count = 0;
1528 while (*modes) // they're sorted biggest to smallest, so find end...
1529 {
1530 modes++;
1531 count++;
1532 }
1533
1534 while (count--)
1535 {
1536 modes--;
1537 SDL_Rect *r = *modes;
1538 int w = r->w;
1539 int h = r->h;
1540 if ((w >= 800) && (h >= 600))
1541 {
1542 // make sure we don't add the same resolution multiple times!
1543 if ( (mNumSupportedResolutions == 0) ||
1544 ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
1545 (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
1546 {
1547 mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
1548 mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
1549 mNumSupportedResolutions++;
1550 }
1551 }
1552 }
1553 }
1554 }
1555
1556 num_resolutions = mNumSupportedResolutions;
1557 return mSupportedResolutions;
1558}
1559
1560BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
1561{
1562 if (!to)
1563 return FALSE;
1564
1565 to->mX = from.mX;
1566 to->mY = mWindow->h - from.mY - 1;
1567
1568 return TRUE;
1569}
1570
1571BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
1572{
1573 if (!to)
1574 return FALSE;
1575
1576 to->mX = from.mX;
1577 to->mY = mWindow->h - from.mY - 1;
1578
1579 return TRUE;
1580}
1581
1582BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
1583{
1584 if (!to)
1585 return FALSE;
1586
1587 // In the fullscreen case, window and screen coordinates are the same.
1588 to->mX = from.mX;
1589 to->mY = from.mY;
1590 return (TRUE);
1591}
1592
1593BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
1594{
1595 if (!to)
1596 return FALSE;
1597
1598 // In the fullscreen case, window and screen coordinates are the same.
1599 to->mX = from.mX;
1600 to->mY = from.mY;
1601 return (TRUE);
1602}
1603
1604BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
1605{
1606 LLCoordWindow window_coord;
1607
1608 return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
1609}
1610
1611BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
1612{
1613 LLCoordWindow window_coord;
1614
1615 return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
1616}
1617
1618
1619
1620
1621void LLWindowSDL::setupFailure(const char* text, const char* caption, U32 type)
1622{
1623 destroyContext();
1624
1625 OSMessageBox(text, caption, type);
1626}
1627
1628BOOL LLWindowSDL::SDLReallyCaptureInput(BOOL capture)
1629{
1630 // note: this used to be safe to call nestedly, but in the
1631 // end that's not really a wise usage pattern, so don't.
1632
1633 if (capture)
1634 mReallyCapturedCount = 1;
1635 else
1636 mReallyCapturedCount = 0;
1637
1638 SDL_GrabMode wantmode, newmode;
1639 if (mReallyCapturedCount <= 0) // uncapture
1640 {
1641 wantmode = SDL_GRAB_OFF;
1642 } else // capture
1643 {
1644 wantmode = SDL_GRAB_ON;
1645 }
1646
1647 if (mReallyCapturedCount < 0) // yuck, imbalance.
1648 {
1649 mReallyCapturedCount = 0;
1650 llwarns << "ReallyCapture count was < 0" << llendl;
1651 }
1652
1653 if (!mFullscreen) /* only bother if we're windowed anyway */
1654 {
1655#if LL_X11
1656 if (SDL_Display)
1657 {
1658 /* we dirtily mix raw X11 with SDL so that our pointer
1659 isn't (as often) constrained to the limits of the
1660 window while grabbed, which feels nicer and
1661 hopefully eliminates some reported 'sticky pointer'
1662 problems. We use raw X11 instead of
1663 SDL_WM_GrabInput() because the latter constrains
1664 the pointer to the window and also steals all
1665 *keyboard* input from the window manager, which was
1666 frustrating users. */
1667 int result;
1668 if (wantmode == SDL_GRAB_ON)
1669 {
1670 //llinfos << "X11 POINTER GRABBY" << llendl;
1671 //newmode = SDL_WM_GrabInput(wantmode);
1672 result = XGrabPointer(SDL_Display, mSDL_XWindowID,
1673 True, 0, GrabModeAsync,
1674 GrabModeAsync,
1675 None, None, CurrentTime);
1676 if (GrabSuccess == result)
1677 newmode = SDL_GRAB_ON;
1678 else
1679 newmode = SDL_GRAB_OFF;
1680 } else if (wantmode == SDL_GRAB_OFF)
1681 {
1682 //llinfos << "X11 POINTER UNGRABBY" << llendl;
1683 newmode = SDL_GRAB_OFF;
1684 //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
1685
1686 XUngrabPointer(SDL_Display, CurrentTime);
1687 // Make sure the ungrab happens RIGHT NOW.
1688 XSync(SDL_Display, False);
1689 } else
1690 {
1691 newmode = SDL_GRAB_QUERY; // neutral
1692 }
1693 } else // not actually running on X11, for some reason
1694 newmode = wantmode;
1695#endif // LL_X11
1696 } else {
1697 // pretend we got what we wanted, when really we don't care.
1698 newmode = wantmode;
1699 }
1700
1701 // return boolean success for whether we ended up in the desired state
1702 return (capture && SDL_GRAB_ON==newmode) ||
1703 (!capture && SDL_GRAB_OFF==newmode);
1704}
1705
1706U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain)
1707{
1708 /* part of the fix for SL-13243: Some popular window managers like
1709 to totally eat alt-drag for the purposes of moving windows. We
1710 spoil their day by acquiring the exclusive X11 mouse lock for as
1711 long as LALT is held down, so the window manager can't easily
1712 see what's happening. Tested successfully with Metacity.
1713 And... do the same with CTRL, for other darn WMs. We don't
1714 care about other metakeys as SL doesn't use them with dragging
1715 (for now). */
1716
1717 /* We maintain a bitmap of critical keys which are up and down
1718 instead of simply key-counting, because SDL sometimes reports
1719 misbalanced keyup/keydown event pairs to us for whatever reason. */
1720
1721 U32 mask = 0;
1722 switch (keysym)
1723 {
1724 case SDLK_LALT:
1725 mask = 1U << 0; break;
1726 case SDLK_LCTRL:
1727 mask = 1U << 1; break;
1728 case SDLK_RCTRL:
1729 mask = 1U << 2; break;
1730 default:
1731 break;
1732 }
1733
1734 if (gain)
1735 mGrabbyKeyFlags |= mask;
1736 else
1737 mGrabbyKeyFlags &= ~mask;
1738
1739 //llinfos << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << llendl;
1740
1741 /* 0 means we don't need to mousegrab, otherwise grab. */
1742 return mGrabbyKeyFlags;
1743}
1744
1745void LLWindowSDL::gatherInput()
1746{
1747 const Uint32 CLICK_THRESHOLD = 300; // milliseconds
1748 static int leftClick = 0;
1749 static int rightClick = 0;
1750 static Uint32 lastLeftDown = 0;
1751 static Uint32 lastRightDown = 0;
1752 SDL_Event event;
1753
1754 while (SDL_PollEvent(&event))
1755 {
1756 switch (event.type)
1757 {
1758 case SDL_MOUSEMOTION:
1759 {
1760 LLCoordWindow winCoord(event.button.x, event.button.y);
1761 LLCoordGL openGlCoord;
1762 convertCoords(winCoord, &openGlCoord);
1763 MASK mask = gKeyboard->currentMask(TRUE);
1764 mCallbacks->handleMouseMove(this, openGlCoord, mask);
1765 break;
1766 }
1767
1768 case SDL_KEYDOWN:
1769 gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod);
1770 // part of the fix for SL-13243
1771 if (SDLCheckGrabbyKeys(event.key.keysym.sym, TRUE) != 0)
1772 SDLReallyCaptureInput(TRUE);
1773
1774 if (event.key.keysym.unicode)
1775 mCallbacks->handleUnicodeChar(event.key.keysym.unicode, gKeyboard->currentMask(FALSE));
1776 break;
1777
1778 case SDL_KEYUP:
1779 if (SDLCheckGrabbyKeys(event.key.keysym.sym, FALSE) == 0)
1780 SDLReallyCaptureInput(FALSE); // part of the fix for SL-13243
1781
1782 // This is a testing hack to pop up a dialog when 4 is pressed
1783 //if (event.key.keysym.sym == SDLK_4)
1784 //OSMessageBox("a whole bunch of text goes right here, whee! test test test.", "this is the title!", OSMB_YESNO);
1785
1786 gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod);
1787 break;
1788
1789 case SDL_MOUSEBUTTONDOWN:
1790 {
1791 bool isDoubleClick = false;
1792 LLCoordWindow winCoord(event.button.x, event.button.y);
1793 LLCoordGL openGlCoord;
1794 convertCoords(winCoord, &openGlCoord);
1795 MASK mask = gKeyboard->currentMask(TRUE);
1796
1797 if (event.button.button == SDL_BUTTON_LEFT) // SDL doesn't manage double clicking...
1798 {
1799 Uint32 now = SDL_GetTicks();
1800 if ((now - lastLeftDown) > CLICK_THRESHOLD)
1801 leftClick = 1;
1802 else
1803 {
1804 if (++leftClick >= 2)
1805 {
1806 leftClick = 0;
1807 isDoubleClick = true;
1808 }
1809 }
1810 lastLeftDown = now;
1811 }
1812 else if (event.button.button == SDL_BUTTON_RIGHT)
1813 {
1814 Uint32 now = SDL_GetTicks();
1815 if ((now - lastRightDown) > CLICK_THRESHOLD)
1816 rightClick = 1;
1817 else
1818 {
1819 if (++rightClick >= 2)
1820 {
1821 rightClick = 0;
1822 isDoubleClick = true;
1823 }
1824 }
1825 lastRightDown = now;
1826 }
1827
1828 if (event.button.button == SDL_BUTTON_LEFT) // left
1829 {
1830 if (isDoubleClick)
1831 mCallbacks->handleDoubleClick(this, openGlCoord, mask);
1832 else
1833 mCallbacks->handleMouseDown(this, openGlCoord, mask);
1834 }
1835
1836 else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
1837 {
1838 // right double click isn't handled right now in Second Life ... if (isDoubleClick)
1839 mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
1840 }
1841
1842 else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
1843 ; // Middle mouse isn't handled right now in Second Life ... mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
1844 else if (event.button.button == 4) // mousewheel up...thanks to X11 for making SDL consider these "buttons".
1845 mCallbacks->handleScrollWheel(this, -1);
1846 else if (event.button.button == 5) // mousewheel down...thanks to X11 for making SDL consider these "buttons".
1847 mCallbacks->handleScrollWheel(this, 1);
1848
1849 break;
1850 }
1851
1852 case SDL_MOUSEBUTTONUP:
1853 {
1854 LLCoordWindow winCoord(event.button.x, event.button.y);
1855 LLCoordGL openGlCoord;
1856 convertCoords(winCoord, &openGlCoord);
1857 MASK mask = gKeyboard->currentMask(TRUE);
1858
1859 if (event.button.button == SDL_BUTTON_LEFT) // left
1860 mCallbacks->handleMouseUp(this, openGlCoord, mask);
1861 else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
1862 mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
1863 else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
1864 ; // UNUSED IN SECOND LIFE RIGHT NOW mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
1865
1866 // don't handle mousewheel here...
1867
1868 break;
1869 }
1870
1871 case SDL_VIDEOEXPOSE: // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing!
1872 mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h);
1873 break;
1874
1875 case SDL_VIDEORESIZE: // *FIX: handle this?
1876 llinfos << "Handling a resize event: " << event.resize.w <<
1877 "x" << event.resize.h << llendl;
1878
1879 // *FIX: I'm not sure this is necessary!
1880 mWindow = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, mSDLFlags);
1881 if (!mWindow)
1882 {
1883 // *FIX: More informative dialog?
1884 llinfos << "Could not recreate context after resize! Quitting..." << llendl;
1885 if(mCallbacks->handleCloseRequest(this))
1886 {
1887 // Get the app to initiate cleanup.
1888 mCallbacks->handleQuit(this);
1889 // The app is responsible for calling destroyWindow when done with GL
1890 }
1891 break;
1892 }
1893
1894 mCallbacks->handleResize(this, event.resize.w, event.resize.h );
1895 break;
1896
1897 case SDL_ACTIVEEVENT:
1898 if (event.active.state & SDL_APPINPUTFOCUS)
1899 {
1900 // Note that for SDL (particularly on X11), keyboard
1901 // and mouse focus are independent things. Here we are
1902 // tracking keyboard focus state changes.
1903
1904 // We have to do our own state massaging because SDL
1905 // can send us two unfocus events in a row for example,
1906 // which confuses the focus code [SL-24071].
1907 if (event.active.gain != mHaveInputFocus)
1908 {
1909 if (event.active.gain)
1910 mCallbacks->handleFocus(this);
1911 else
1912 mCallbacks->handleFocusLost(this);
1913
1914 mHaveInputFocus = !!event.active.gain;
1915 }
1916 }
1917 if (event.active.state & SDL_APPACTIVE)
1918 {
1919 // Change in iconification/minimization state.
1920 if ((!event.active.gain) != mIsMinimized)
1921 {
1922 mCallbacks->handleActivate(this, !!event.active.gain);
1923 llinfos << "SDL deiconification state switched to " << BOOL(event.active.gain) << llendl;
1924
1925 mIsMinimized = (!event.active.gain);
1926 }
1927 else
1928 {
1929 llinfos << "Ignored bogus redundant SDL deiconification state switch to " << BOOL(event.active.gain) << llendl;
1930 }
1931 }
1932 break;
1933
1934 case SDL_QUIT:
1935 if(mCallbacks->handleCloseRequest(this))
1936 {
1937 // Get the app to initiate cleanup.
1938 mCallbacks->handleQuit(this);
1939 // The app is responsible for calling destroyWindow when done with GL
1940 }
1941 break;
1942 default:
1943 //llinfos << "Unhandled SDL event type " << event.type << llendl;
1944 break;
1945 }
1946 }
1947
1948#if LL_X11
1949 // This is a good time to stop flashing the icon if our mFlashTimer has
1950 // expired.
1951 if (mFlashing && mFlashTimer.hasExpired())
1952 {
1953 x11_set_urgent(FALSE);
1954 mFlashing = FALSE;
1955 }
1956#endif // LL_X11
1957}
1958
1959static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty)
1960{
1961 SDL_Cursor *sdlcursor = NULL;
1962 SDL_Surface *bmpsurface;
1963
1964 // Load cursor pixel data from BMP file
1965 bmpsurface = Load_BMP_Resource(filename);
1966 if (bmpsurface && bmpsurface->w%8==0)
1967 {
1968 SDL_Surface *cursurface;
1969 llinfos << "Loaded cursor file " << filename << " "
1970 << bmpsurface->w << "x" << bmpsurface->h << llendl;
1971 cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE,
1972 bmpsurface->w,
1973 bmpsurface->h,
1974 32,
1975 0xFFU,
1976 0xFF00U,
1977 0xFF0000U,
1978 0xFF000000U);
1979 SDL_FillRect(cursurface, NULL, 0x00000000U);
1980
1981 // Blit the cursor pixel data onto a 32-bit RGBA surface so we
1982 // only have to cope with processing one type of pixel format.
1983 if (0 == SDL_BlitSurface(bmpsurface, NULL,
1984 cursurface, NULL))
1985 {
1986 // n.b. we already checked that width is a multiple of 8.
1987 const int bitmap_bytes = (cursurface->w * cursurface->h) / 8;
1988 unsigned char *cursor_data = new unsigned char[bitmap_bytes];
1989 unsigned char *cursor_mask = new unsigned char[bitmap_bytes];
1990 memset(cursor_data, 0, bitmap_bytes);
1991 memset(cursor_mask, 0, bitmap_bytes);
1992 int i,j;
1993 // Walk the RGBA cursor pixel data, extracting both data and
1994 // mask to build SDL-friendly cursor bitmaps from. The mask
1995 // is inferred by color-keying against 200,200,200
1996 for (i=0; i<cursurface->h; ++i) {
1997 for (j=0; j<cursurface->w; ++j) {
1998 unsigned char *pixelp =
1999 ((unsigned char *)cursurface->pixels)
2000 + cursurface->pitch * i
2001 + j*cursurface->format->BytesPerPixel;
2002 unsigned char srcred = pixelp[0];
2003 unsigned char srcgreen = pixelp[1];
2004 unsigned char srcblue = pixelp[2];
2005 BOOL mask_bit = (srcred != 200)
2006 || (srcgreen != 200)
2007 || (srcblue != 200);
2008 BOOL data_bit = mask_bit && (srcgreen <= 80);//not 0x80
2009 unsigned char bit_offset = (cursurface->w/8) * i
2010 + j/8;
2011 cursor_data[bit_offset] |= (data_bit) << (7 - (j&7));
2012 cursor_mask[bit_offset] |= (mask_bit) << (7 - (j&7));
2013 }
2014 }
2015 sdlcursor = SDL_CreateCursor((Uint8*)cursor_data,
2016 (Uint8*)cursor_mask,
2017 cursurface->w, cursurface->h,
2018 hotx, hoty);
2019 delete[] cursor_data;
2020 delete[] cursor_mask;
2021 } else {
2022 llwarns << "CURSOR BLIT FAILURE, cursurface: " << cursurface << llendl;
2023 }
2024 SDL_FreeSurface(cursurface);
2025 SDL_FreeSurface(bmpsurface);
2026 } else {
2027 llwarns << "CURSOR LOAD FAILURE " << filename << llendl;
2028 }
2029
2030 return sdlcursor;
2031}
2032
2033void LLWindowSDL::setCursor(ECursorType cursor)
2034{
2035 if (mCurrentCursor != cursor)
2036 {
2037 if (cursor < UI_CURSOR_COUNT)
2038 {
2039 SDL_Cursor *sdlcursor = mSDLCursors[cursor];
2040 // Try to default to the arrow for any cursors that
2041 // did not load correctly.
2042 if (!sdlcursor && mSDLCursors[UI_CURSOR_ARROW])
2043 sdlcursor = mSDLCursors[UI_CURSOR_ARROW];
2044 if (sdlcursor)
2045 SDL_SetCursor(sdlcursor);
2046 } else {
2047 llwarns << "Tried to set invalid cursor number " << cursor << llendl;
2048 }
2049 mCurrentCursor = cursor;
2050 }
2051}
2052
2053ECursorType LLWindowSDL::getCursor()
2054{
2055 return mCurrentCursor;
2056}
2057
2058void LLWindowSDL::initCursors()
2059{
2060 int i;
2061 // Blank the cursor pointer array for those we may miss.
2062 for (i=0; i<UI_CURSOR_COUNT; ++i)
2063 {
2064 mSDLCursors[i] = NULL;
2065 }
2066 // Pre-make an SDL cursor for each of the known cursor types.
2067 // We hardcode the hotspots - to avoid that we'd have to write
2068 // a .cur file loader.
2069 // NOTE: SDL doesn't load RLE-compressed BMP files.
2070 mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0);
2071 mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15);
2072 mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10);
2073 mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16);
2074 mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14);
2075 mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17);
2076 mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17);
2077 mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14);
2078 mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16);
2079 mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
2080 mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
2081 mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13);
2082 mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6);
2083 mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5);
2084 mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7);
2085 mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0);
2086 mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0);
2087 mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0);
2088 mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0);
2089 mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8);
2090 mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0);
2091 mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13);
2092 mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0);
2093 mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0);
2094 mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0);
2095 mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5);
2096 mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5);
2097 mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5);
2098 mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0);
2099 mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",0,0);
2100 mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",0,0);
2101 mSDLCursors[UI_CURSOR_TOOLPAY] = makeSDLCursorFromBMP("toolpay.BMP",0,0);
2102 mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",0,0);
2103 mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
2104}
2105
2106void LLWindowSDL::quitCursors()
2107{
2108 int i;
2109 if (mWindow)
2110 {
2111 for (i=0; i<UI_CURSOR_COUNT; ++i)
2112 {
2113 if (mSDLCursors[i])
2114 {
2115 SDL_FreeCursor(mSDLCursors[i]);
2116 mSDLCursors[i] = NULL;
2117 }
2118 }
2119 } else {
2120 // SDL doesn't refcount cursors, so if the window has
2121 // already been destroyed then the cursors have gone with it.
2122 llinfos << "Skipping quitCursors: mWindow already gone." << llendl;
2123 for (i=0; i<UI_CURSOR_COUNT; ++i)
2124 mSDLCursors[i] = NULL;
2125 }
2126}
2127
2128void LLWindowSDL::captureMouse()
2129{
2130 // SDL already enforces the semantics that captureMouse is
2131 // used for, i.e. that we continue to get mouse events as long
2132 // as a button is down regardless of whether we left the
2133 // window, and in a less obnoxious way than SDL_WM_GrabInput
2134 // which would confine the cursor to the window too.
2135
2136 //llinfos << "LLWindowSDL::captureMouse" << llendl;
2137}
2138
2139void LLWindowSDL::releaseMouse()
2140{
2141 // see LWindowSDL::captureMouse()
2142
2143 //llinfos << "LLWindowSDL::releaseMouse" << llendl;
2144}
2145
2146void LLWindowSDL::hideCursor()
2147{
2148 if(!mCursorHidden)
2149 {
2150 // llinfos << "hideCursor: hiding" << llendl;
2151 mCursorHidden = TRUE;
2152 mHideCursorPermanent = TRUE;
2153 SDL_ShowCursor(0);
2154 }
2155 else
2156 {
2157 // llinfos << "hideCursor: already hidden" << llendl;
2158 }
2159
2160 adjustCursorDecouple();
2161}
2162
2163void LLWindowSDL::showCursor()
2164{
2165 if(mCursorHidden)
2166 {
2167 // llinfos << "showCursor: showing" << llendl;
2168 mCursorHidden = FALSE;
2169 mHideCursorPermanent = FALSE;
2170 SDL_ShowCursor(1);
2171 }
2172 else
2173 {
2174 // llinfos << "showCursor: already visible" << llendl;
2175 }
2176
2177 adjustCursorDecouple();
2178}
2179
2180void LLWindowSDL::showCursorFromMouseMove()
2181{
2182 if (!mHideCursorPermanent)
2183 {
2184 showCursor();
2185 }
2186}
2187
2188void LLWindowSDL::hideCursorUntilMouseMove()
2189{
2190 if (!mHideCursorPermanent)
2191 {
2192 hideCursor();
2193 mHideCursorPermanent = FALSE;
2194 }
2195}
2196
2197
2198
2199//
2200// LLSplashScreenSDL
2201//
2202LLSplashScreenSDL::LLSplashScreenSDL()
2203{
2204}
2205
2206LLSplashScreenSDL::~LLSplashScreenSDL()
2207{
2208}
2209
2210void LLSplashScreenSDL::showImpl()
2211{
2212}
2213
2214void LLSplashScreenSDL::updateImpl(const char* mesg)
2215{
2216}
2217
2218
2219void LLSplashScreenSDL::hideImpl()
2220{
2221}
2222
2223
2224
2225#if LL_GTK
2226static void response_callback (GtkDialog *dialog,
2227 gint arg1,
2228 gpointer user_data)
2229{
2230 gint *response = (gint*)user_data;
2231 *response = arg1;
2232 gtk_widget_destroy(GTK_WIDGET(dialog));
2233 gtk_main_quit();
2234}
2235
2236S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
2237{
2238 S32 rtn = OSBTN_CANCEL;
2239
2240#if LL_GTK
2241 maybe_do_gtk_diagnostics();
2242#endif // LL_GTK
2243
2244 if(gWindowImplementation != NULL)
2245 gWindowImplementation->beforeDialog();
2246
2247 gtk_disable_setlocale();
2248 if (gtk_init_check(NULL, NULL)
2249 // We can NOT expect to combine GTK and SDL's aggressive fullscreen
2250 && ((NULL==gWindowImplementation) || (!was_fullscreen))
2251 )
2252 {
2253 GtkWidget *win = NULL;
2254
2255 llinfos << "Creating a dialog because we're in windowed mode and GTK is happy." << llendl;
2256
2257 GtkDialogFlags flags = GTK_DIALOG_MODAL;
2258 GtkMessageType messagetype;
2259 GtkButtonsType buttons;
2260 switch (type)
2261 {
2262 default:
2263 case OSMB_OK:
2264 messagetype = GTK_MESSAGE_WARNING;
2265 buttons = GTK_BUTTONS_OK;
2266 break;
2267 case OSMB_OKCANCEL:
2268 messagetype = GTK_MESSAGE_QUESTION;
2269 buttons = GTK_BUTTONS_OK_CANCEL;
2270 break;
2271 case OSMB_YESNO:
2272 messagetype = GTK_MESSAGE_QUESTION;
2273 buttons = GTK_BUTTONS_YES_NO;
2274 break;
2275 }
2276 win = gtk_message_dialog_new(NULL,
2277 flags, messagetype, buttons,
2278 text);
2279
2280# if LL_X11
2281 // Make GTK tell the window manager to associate this
2282 // dialog with our non-GTK SDL window, which should try
2283 // to keep it on top etc.
2284 if (SDL_XWindowID != None)
2285 {
2286 gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
2287 GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
2288 gdk_window_set_transient_for(GTK_WIDGET(win)->window,
2289 gdkwin);
2290 }
2291# endif //LL_X11
2292
2293 gtk_window_set_position(GTK_WINDOW(win),
2294 GTK_WIN_POS_CENTER_ON_PARENT);
2295
2296 gtk_window_set_type_hint(GTK_WINDOW(win),
2297 GDK_WINDOW_TYPE_HINT_DIALOG);
2298
2299 if (caption)
2300 gtk_window_set_title(GTK_WINDOW(win), caption);
2301
2302 gint response = GTK_RESPONSE_NONE;
2303 g_signal_connect (win,
2304 "response",
2305 G_CALLBACK (response_callback),
2306 &response);
2307
2308 // we should be able to us a gtk_dialog_run(), but it's
2309 // apparently not written to exist in a world without a higher
2310 // gtk_main(), so we manage its signal/destruction outselves.
2311 gtk_widget_show_all (win);
2312 gtk_main();
2313
2314 //llinfos << "response: " << response << llendl;
2315 switch (response)
2316 {
2317 case GTK_RESPONSE_OK: rtn = OSBTN_OK; break;
2318 case GTK_RESPONSE_YES: rtn = OSBTN_YES; break;
2319 case GTK_RESPONSE_NO: rtn = OSBTN_NO; break;
2320 case GTK_RESPONSE_APPLY: rtn = OSBTN_OK; break;
2321 case GTK_RESPONSE_NONE:
2322 case GTK_RESPONSE_CANCEL:
2323 case GTK_RESPONSE_CLOSE:
2324 case GTK_RESPONSE_DELETE_EVENT:
2325 default: rtn = OSBTN_CANCEL;
2326 }
2327 }
2328 else
2329 {
2330 fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
2331 llinfos << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << llendl;
2332 rtn = OSBTN_OK;
2333 }
2334
2335 if(gWindowImplementation != NULL)
2336 gWindowImplementation->afterDialog();
2337
2338 return rtn;
2339}
2340
2341static void color_changed_callback(GtkWidget *widget,
2342 gpointer user_data)
2343{
2344 GtkColorSelection *colorsel = GTK_COLOR_SELECTION(widget);
2345 GdkColor *colorp = (GdkColor*)user_data;
2346
2347 gtk_color_selection_get_current_color(colorsel, colorp);
2348}
2349
2350BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
2351{
2352 BOOL rtn = FALSE;
2353
2354 beforeDialog();
2355
2356 gtk_disable_setlocale();
2357 if (gtk_init_check(NULL, NULL)
2358 // We can NOT expect to combine GTK and SDL's aggressive fullscreen
2359 && !was_fullscreen
2360 )
2361 {
2362 GtkWidget *win = NULL;
2363
2364 win = gtk_color_selection_dialog_new(NULL);
2365
2366# if LL_X11
2367 // Get GTK to tell the window manager to associate this
2368 // dialog with our non-GTK SDL window, which should try
2369 // to keep it on top etc.
2370 if (SDL_XWindowID != None)
2371 {
2372 gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
2373 GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
2374 gdk_window_set_transient_for(GTK_WIDGET(win)->window,
2375 gdkwin);
2376 }
2377# endif //LL_X11
2378
2379 GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel);
2380
2381 GdkColor color, orig_color;
2382 orig_color.red = guint16(65535 * *r);
2383 orig_color.green= guint16(65535 * *g);
2384 orig_color.blue = guint16(65535 * *b);
2385 color = orig_color;
2386
2387 gtk_color_selection_set_previous_color (colorsel, &color);
2388 gtk_color_selection_set_current_color (colorsel, &color);
2389 gtk_color_selection_set_has_palette (colorsel, TRUE);
2390 gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
2391
2392 gint response = GTK_RESPONSE_NONE;
2393 g_signal_connect (win,
2394 "response",
2395 G_CALLBACK (response_callback),
2396 &response);
2397
2398 g_signal_connect (G_OBJECT (colorsel), "color_changed",
2399 G_CALLBACK (color_changed_callback),
2400 &color);
2401
2402 gtk_window_set_modal(GTK_WINDOW(win), TRUE);
2403 gtk_widget_show_all(win);
2404 // hide the help button - we don't service it.
2405 gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button);
2406 gtk_main();
2407
2408 if (response == GTK_RESPONSE_OK &&
2409 (orig_color.red != color.red
2410 || orig_color.green != color.green
2411 || orig_color.blue != color.blue) )
2412 {
2413 *r = color.red / 65535.0f;
2414 *g = color.green / 65535.0f;
2415 *b = color.blue / 65535.0f;
2416 rtn = TRUE;
2417 }
2418 }
2419
2420 afterDialog();
2421
2422 return rtn;
2423}
2424#else
2425S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
2426{
2427 fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
2428 return 0;
2429}
2430
2431BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
2432{
2433 return (FALSE);
2434}
2435#endif // LL_GTK
2436
2437// Open a URL with the user's default web browser.
2438// Must begin with protocol identifier.
2439void spawn_web_browser(const char* escaped_url)
2440{
2441 llinfos << "spawn_web_browser: " << escaped_url << llendl;
2442
2443#if LL_LINUX
2444# if LL_X11
2445 if (SDL_Display) // Just in case - before forking.
2446 XSync(SDL_Display, False);
2447# endif // LL_X11
2448
2449 std::string cmd;
2450 cmd = gDirUtilp->getAppRODataDir().c_str();
2451 cmd += gDirUtilp->getDirDelimiter().c_str();
2452 cmd += "launch_url.sh";
2453 char* const argv[] = {(char*)cmd.c_str(), (char*)escaped_url, NULL};
2454
2455 pid_t pid = fork();
2456 if (pid == 0)
2457 { // child
2458 // disconnect from stdin/stdout/stderr, or child will
2459 // keep our output pipe undesirably alive if it outlives us.
2460 close(0);
2461 close(1);
2462 close(2);
2463 // end ourself by running the command
2464 execv(cmd.c_str(), argv);
2465 // if execv returns at all, there was a problem.
2466 llwarns << "execv failure when trying to start " << cmd << llendl;
2467 _exit(1); // _exit because we don't want atexit() clean-up!
2468 } else {
2469 if (pid > 0)
2470 {
2471 // parent - wait for child to die
2472 int childExitStatus;
2473 waitpid(pid, &childExitStatus, 0);
2474 } else {
2475 llwarns << "fork failure." << llendl;
2476 }
2477 }
2478#endif // LL_LINUX
2479
2480 llinfos << "spawn_web_browser returning." << llendl;
2481}
2482
2483void shell_open( const char* file_path )
2484{
2485 // *FIX: (???)
2486 fprintf(stderr, "shell_open: %s\n", file_path);
2487}
2488
2489void *LLWindowSDL::getPlatformWindow()
2490{
2491#if LL_X11
2492 // pointer to our static raw X window
2493 return (void*)&SDL_XWindowID;
2494#else
2495 // doubt we really want to return a high-level SDL structure here.
2496 return NULL;
2497#endif
2498}
2499
2500void LLWindowSDL::bringToFront()
2501{
2502 // *FIX: (???)
2503 fprintf(stderr, "bringToFront\n");
2504}
2505
2506#endif // LL_SDL