diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llwindow/llwindowsdl.cpp | |
parent | README.txt (diff) | |
download | meta-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.cpp | 2506 |
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 | |||
55 | extern 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. | ||
61 | const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */ | ||
62 | const S32 BITS_PER_PIXEL = 32; | ||
63 | const 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. | ||
76 | static Display *SDL_Display = NULL; | ||
77 | static 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. | ||
85 | static LLWindowSDL *gWindowImplementation = NULL; | ||
86 | |||
87 | static BOOL was_fullscreen = FALSE; | ||
88 | |||
89 | // Cross-platform bits: | ||
90 | |||
91 | void 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. | ||
118 | static 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 | |||
152 | BOOL 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 | |||
188 | LLWindowSDL::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 | |||
242 | static 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 | |||
258 | BOOL 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. | ||
563 | BOOL 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 | |||
589 | void 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 | |||
606 | LLWindowSDL::~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 | |||
622 | void LLWindowSDL::show() | ||
623 | { | ||
624 | // *FIX: What to do with SDL? | ||
625 | } | ||
626 | |||
627 | void LLWindowSDL::hide() | ||
628 | { | ||
629 | // *FIX: What to do with SDL? | ||
630 | } | ||
631 | |||
632 | void LLWindowSDL::minimize() | ||
633 | { | ||
634 | // *FIX: What to do with SDL? | ||
635 | } | ||
636 | |||
637 | void 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() | ||
645 | void 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 | |||
660 | BOOL LLWindowSDL::isValid() | ||
661 | { | ||
662 | return (mWindow != NULL); | ||
663 | } | ||
664 | |||
665 | BOOL 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 | |||
679 | BOOL LLWindowSDL::getMinimized() | ||
680 | { | ||
681 | BOOL result = FALSE; | ||
682 | |||
683 | if (mWindow && (1 == mIsMinimized)) | ||
684 | { | ||
685 | result = TRUE; | ||
686 | } | ||
687 | return(result); | ||
688 | } | ||
689 | |||
690 | BOOL LLWindowSDL::getMaximized() | ||
691 | { | ||
692 | BOOL result = FALSE; | ||
693 | |||
694 | if (mWindow) | ||
695 | { | ||
696 | // TODO | ||
697 | } | ||
698 | |||
699 | return(result); | ||
700 | } | ||
701 | |||
702 | BOOL LLWindowSDL::maximize() | ||
703 | { | ||
704 | // TODO | ||
705 | return FALSE; | ||
706 | } | ||
707 | |||
708 | BOOL LLWindowSDL::getFullscreen() | ||
709 | { | ||
710 | return mFullscreen; | ||
711 | } | ||
712 | |||
713 | BOOL 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 | |||
721 | BOOL 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 | |||
734 | BOOL 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 | |||
747 | BOOL 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 | |||
758 | BOOL 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 | |||
769 | void LLWindowSDL::swapBuffers() | ||
770 | { | ||
771 | if (mWindow) | ||
772 | SDL_GL_SwapBuffers(); | ||
773 | } | ||
774 | |||
775 | F32 LLWindowSDL::getGamma() | ||
776 | { | ||
777 | return 1/mGamma; | ||
778 | } | ||
779 | |||
780 | BOOL LLWindowSDL::restoreGamma() | ||
781 | { | ||
782 | //CGDisplayRestoreColorSyncSettings(); | ||
783 | SDL_SetGamma(1.0f, 1.0f, 1.0f); | ||
784 | return true; | ||
785 | } | ||
786 | |||
787 | BOOL 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 | |||
796 | BOOL LLWindowSDL::isCursorHidden() | ||
797 | { | ||
798 | return mCursorHidden; | ||
799 | } | ||
800 | |||
801 | |||
802 | |||
803 | // Constrains the mouse to the window. | ||
804 | void 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 | |||
813 | BOOL 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 | |||
833 | BOOL 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 | |||
848 | void 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 | |||
876 | F32 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 | |||
915 | F32 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. | ||
932 | static LLCoordScreen old_size; | ||
933 | static BOOL old_fullscreen; | ||
934 | void 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 | |||
966 | void 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 | |||
979 | S32 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 | ||
986 | void 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 | |||
1010 | void 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 | |||
1065 | typedef Atom x11clipboard_type; | ||
1066 | |||
1067 | static | ||
1068 | x11clipboard_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. */ | ||
1092 | static int | ||
1093 | convert_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. */ | ||
1124 | static int | ||
1125 | convert_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 | |||
1153 | int | ||
1154 | LLWindowSDL::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 | |||
1165 | void | ||
1166 | LLWindowSDL::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 | |||
1212 | void | ||
1213 | LLWindowSDL::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 | |||
1299 | int 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 | |||
1379 | int | ||
1380 | LLWindowSDL::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 | |||
1415 | void | ||
1416 | LLWindowSDL::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 | |||
1429 | BOOL LLWindowSDL::isClipboardTextAvailable() | ||
1430 | { | ||
1431 | return !is_empty_x11clipboard(); | ||
1432 | } | ||
1433 | |||
1434 | BOOL 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 | |||
1469 | BOOL 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 | |||
1492 | BOOL LLWindowSDL::isClipboardTextAvailable() | ||
1493 | { | ||
1494 | return FALSE; // unsupported | ||
1495 | } | ||
1496 | |||
1497 | BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst) | ||
1498 | { | ||
1499 | return FALSE; // unsupported | ||
1500 | } | ||
1501 | |||
1502 | BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s) | ||
1503 | { | ||
1504 | return FALSE; // unsupported | ||
1505 | } | ||
1506 | #endif // LL_X11 | ||
1507 | |||
1508 | BOOL 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 | |||
1517 | LLWindow::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 | |||
1560 | BOOL 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 | |||
1571 | BOOL 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 | |||
1582 | BOOL 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 | |||
1593 | BOOL 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 | |||
1604 | BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to) | ||
1605 | { | ||
1606 | LLCoordWindow window_coord; | ||
1607 | |||
1608 | return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); | ||
1609 | } | ||
1610 | |||
1611 | BOOL 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 | |||
1621 | void LLWindowSDL::setupFailure(const char* text, const char* caption, U32 type) | ||
1622 | { | ||
1623 | destroyContext(); | ||
1624 | |||
1625 | OSMessageBox(text, caption, type); | ||
1626 | } | ||
1627 | |||
1628 | BOOL 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 | |||
1706 | U32 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 | |||
1745 | void 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 | |||
1959 | static 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 | |||
2033 | void 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 | |||
2053 | ECursorType LLWindowSDL::getCursor() | ||
2054 | { | ||
2055 | return mCurrentCursor; | ||
2056 | } | ||
2057 | |||
2058 | void 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 | |||
2106 | void 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 | |||
2128 | void 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 | |||
2139 | void LLWindowSDL::releaseMouse() | ||
2140 | { | ||
2141 | // see LWindowSDL::captureMouse() | ||
2142 | |||
2143 | //llinfos << "LLWindowSDL::releaseMouse" << llendl; | ||
2144 | } | ||
2145 | |||
2146 | void 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 | |||
2163 | void 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 | |||
2180 | void LLWindowSDL::showCursorFromMouseMove() | ||
2181 | { | ||
2182 | if (!mHideCursorPermanent) | ||
2183 | { | ||
2184 | showCursor(); | ||
2185 | } | ||
2186 | } | ||
2187 | |||
2188 | void LLWindowSDL::hideCursorUntilMouseMove() | ||
2189 | { | ||
2190 | if (!mHideCursorPermanent) | ||
2191 | { | ||
2192 | hideCursor(); | ||
2193 | mHideCursorPermanent = FALSE; | ||
2194 | } | ||
2195 | } | ||
2196 | |||
2197 | |||
2198 | |||
2199 | // | ||
2200 | // LLSplashScreenSDL | ||
2201 | // | ||
2202 | LLSplashScreenSDL::LLSplashScreenSDL() | ||
2203 | { | ||
2204 | } | ||
2205 | |||
2206 | LLSplashScreenSDL::~LLSplashScreenSDL() | ||
2207 | { | ||
2208 | } | ||
2209 | |||
2210 | void LLSplashScreenSDL::showImpl() | ||
2211 | { | ||
2212 | } | ||
2213 | |||
2214 | void LLSplashScreenSDL::updateImpl(const char* mesg) | ||
2215 | { | ||
2216 | } | ||
2217 | |||
2218 | |||
2219 | void LLSplashScreenSDL::hideImpl() | ||
2220 | { | ||
2221 | } | ||
2222 | |||
2223 | |||
2224 | |||
2225 | #if LL_GTK | ||
2226 | static 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 | |||
2236 | S32 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 | |||
2341 | static 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 | |||
2350 | BOOL 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 | ||
2425 | S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type) | ||
2426 | { | ||
2427 | fprintf(stderr, "MSGBOX: %s: %s\n", caption, text); | ||
2428 | return 0; | ||
2429 | } | ||
2430 | |||
2431 | BOOL 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. | ||
2439 | void 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 | |||
2483 | void shell_open( const char* file_path ) | ||
2484 | { | ||
2485 | // *FIX: (???) | ||
2486 | fprintf(stderr, "shell_open: %s\n", file_path); | ||
2487 | } | ||
2488 | |||
2489 | void *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 | |||
2500 | void LLWindowSDL::bringToFront() | ||
2501 | { | ||
2502 | // *FIX: (???) | ||
2503 | fprintf(stderr, "bringToFront\n"); | ||
2504 | } | ||
2505 | |||
2506 | #endif // LL_SDL | ||