diff options
Diffstat (limited to 'linden/indra/llwindow/llwindowwin32.cpp')
-rw-r--r-- | linden/indra/llwindow/llwindowwin32.cpp | 3255 |
1 files changed, 3255 insertions, 0 deletions
diff --git a/linden/indra/llwindow/llwindowwin32.cpp b/linden/indra/llwindow/llwindowwin32.cpp new file mode 100644 index 0000000..b405bdb --- /dev/null +++ b/linden/indra/llwindow/llwindowwin32.cpp | |||
@@ -0,0 +1,3255 @@ | |||
1 | /** | ||
2 | * @file llwindowwin32.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 | #include "linden_common.h" | ||
29 | |||
30 | #if LL_WINDOWS && !LL_MESA_HEADLESS | ||
31 | |||
32 | #include "llwindowwin32.h" | ||
33 | |||
34 | #include <commdlg.h> | ||
35 | #include <WinUser.h> | ||
36 | #include <mapi.h> | ||
37 | #include <process.h> // for _spawn | ||
38 | #include <shellapi.h> | ||
39 | |||
40 | // Require DirectInput version 8 | ||
41 | #define DIRECTINPUT_VERSION 0x0800 | ||
42 | #include <dinput.h> | ||
43 | |||
44 | |||
45 | #include "llkeyboardwin32.h" | ||
46 | #include "llerror.h" | ||
47 | #include "llgl.h" | ||
48 | #include "llstring.h" | ||
49 | #include "lldir.h" | ||
50 | |||
51 | #include "llglheaders.h" | ||
52 | |||
53 | #include "indra_constants.h" | ||
54 | |||
55 | // culled from winuser.h | ||
56 | const S32 WM_MOUSEWHEEL = 0x020A; | ||
57 | const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */ | ||
58 | const S32 MAX_MESSAGE_PER_UPDATE = 20; | ||
59 | const S32 BITS_PER_PIXEL = 32; | ||
60 | const S32 MAX_NUM_RESOLUTIONS = 32; | ||
61 | const F32 ICON_FLASH_TIME = 0.5f; | ||
62 | |||
63 | extern BOOL gDebugWindowProc; | ||
64 | |||
65 | LPWSTR gIconResource = IDI_APPLICATION; | ||
66 | |||
67 | LLW32MsgCallback gAsyncMsgCallback = NULL; | ||
68 | |||
69 | // | ||
70 | // LLWindowWin32 | ||
71 | // | ||
72 | |||
73 | void show_window_creation_error(const char* title) | ||
74 | { | ||
75 | llwarns << title << llendl; | ||
76 | shell_open( "help/window_creation_error.html"); | ||
77 | /* | ||
78 | OSMessageBox( | ||
79 | "Second Life is unable to run because it can't set up your display.\n" | ||
80 | "We need to be able to make a 32-bit color window at 1024x768, with\n" | ||
81 | "an 8 bit alpha channel.\n" | ||
82 | "\n" | ||
83 | "First, be sure your monitor is set to True Color (32-bit) in\n" | ||
84 | "Start -> Control Panels -> Display -> Settings.\n" | ||
85 | "\n" | ||
86 | "Otherwise, this may be due to video card driver issues.\n" | ||
87 | "Please make sure you have the latest video card drivers installed.\n" | ||
88 | "ATI drivers are available at http://www.ati.com/\n" | ||
89 | "nVidia drivers are available at http://www.nvidia.com/\n" | ||
90 | "\n" | ||
91 | "If you continue to receive this message, contact customer service.", | ||
92 | title, | ||
93 | OSMB_OK); | ||
94 | */ | ||
95 | } | ||
96 | |||
97 | BOOL check_for_card(const char* RENDERER, const char* bad_card) | ||
98 | { | ||
99 | if (!strnicmp(RENDERER, bad_card, strlen(bad_card))) | ||
100 | { | ||
101 | char buffer[1024]; | ||
102 | sprintf(buffer, | ||
103 | "Your video card appears to be a %s, which Second Life does not support.\n" | ||
104 | "\n" | ||
105 | "Second Life requires a video card with 32 Mb of memory or more, as well as\n" | ||
106 | "multitexture support. We explicitly support nVidia GeForce 2 or better, \n" | ||
107 | "and ATI Radeon 8500 or better.\n" | ||
108 | "\n" | ||
109 | "If you own a supported card and continue to receive this message, try \n" | ||
110 | "updating to the latest video card drivers. Otherwise look in the\n" | ||
111 | "secondlife.com support section or e-mail technical support\n" | ||
112 | "\n" | ||
113 | "You can try to run Second Life, but it will probably crash or run\n" | ||
114 | "very slowly. Try anyway?", | ||
115 | bad_card); | ||
116 | S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO); | ||
117 | if (OSBTN_YES == button) | ||
118 | { | ||
119 | return FALSE; | ||
120 | } | ||
121 | else | ||
122 | { | ||
123 | return TRUE; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | return FALSE; | ||
128 | } | ||
129 | |||
130 | //static | ||
131 | BOOL LLWindowWin32::sIsClassRegistered = FALSE; | ||
132 | |||
133 | |||
134 | |||
135 | LPDIRECTINPUT8 g_pDI = NULL; | ||
136 | LPDIRECTINPUTDEVICE8 g_pJoystick = NULL; | ||
137 | BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, | ||
138 | VOID* pContext ); | ||
139 | BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, | ||
140 | VOID* pContext ); | ||
141 | |||
142 | |||
143 | LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width, | ||
144 | S32 height, U32 flags, | ||
145 | BOOL fullscreen, BOOL clearBg, | ||
146 | BOOL disable_vsync, BOOL use_gl, | ||
147 | BOOL ignore_pixel_depth) | ||
148 | : LLWindow(fullscreen, flags) | ||
149 | { | ||
150 | mIconResource = gIconResource; | ||
151 | mOverrideAspectRatio = 0.f; | ||
152 | mNativeAspectRatio = 0.f; | ||
153 | mMousePositionModified = FALSE; | ||
154 | mInputProcessingPaused = FALSE; | ||
155 | |||
156 | // Initialize the keyboard | ||
157 | gKeyboard = new LLKeyboardWin32(); | ||
158 | |||
159 | GLuint pixel_format; | ||
160 | WNDCLASS wc; | ||
161 | DWORD dw_ex_style; | ||
162 | DWORD dw_style; | ||
163 | RECT window_rect; | ||
164 | |||
165 | // Set the window title | ||
166 | if (!title) | ||
167 | { | ||
168 | mWindowTitle = new WCHAR[50]; | ||
169 | wsprintf(mWindowTitle, L"OpenGL Window"); | ||
170 | } | ||
171 | else | ||
172 | { | ||
173 | mWindowTitle = new WCHAR[256]; // Assume title length < 255 chars. | ||
174 | mbstowcs(mWindowTitle, title, 255); | ||
175 | mWindowTitle[255] = 0; | ||
176 | } | ||
177 | |||
178 | // Set the window class name | ||
179 | if (!name) | ||
180 | { | ||
181 | mWindowClassName = new WCHAR[50]; | ||
182 | wsprintf(mWindowClassName, L"OpenGL Window"); | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | mWindowClassName = new WCHAR[256]; // Assume title length < 255 chars. | ||
187 | mbstowcs(mWindowClassName, name, 255); | ||
188 | mWindowClassName[255] = 0; | ||
189 | } | ||
190 | |||
191 | |||
192 | // We're not clipping yet | ||
193 | SetRect( &mOldMouseClip, 0, 0, 0, 0 ); | ||
194 | |||
195 | // Make an instance of our window then define the window class | ||
196 | mhInstance = GetModuleHandle(NULL); | ||
197 | mWndProc = NULL; | ||
198 | |||
199 | mSwapMethod = SWAP_METHOD_UNDEFINED; | ||
200 | |||
201 | // No WPARAM yet. | ||
202 | mLastSizeWParam = 0; | ||
203 | |||
204 | // Windows GDI rects don't include rightmost pixel | ||
205 | window_rect.left = (long) 0; | ||
206 | window_rect.right = (long) width; | ||
207 | window_rect.top = (long) 0; | ||
208 | window_rect.bottom = (long) height; | ||
209 | |||
210 | // Grab screen size to sanitize the window | ||
211 | S32 window_border_y = GetSystemMetrics(SM_CYBORDER); | ||
212 | S32 virtual_screen_x = GetSystemMetrics(SM_XVIRTUALSCREEN); | ||
213 | S32 virtual_screen_y = GetSystemMetrics(SM_YVIRTUALSCREEN); | ||
214 | S32 virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN); | ||
215 | S32 virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN); | ||
216 | |||
217 | if (x < virtual_screen_x) x = virtual_screen_x; | ||
218 | if (y < virtual_screen_y - window_border_y) y = virtual_screen_y - window_border_y; | ||
219 | |||
220 | if (x + width > virtual_screen_x + virtual_screen_width) x = virtual_screen_x + virtual_screen_width - width; | ||
221 | if (y + height > virtual_screen_y + virtual_screen_height) y = virtual_screen_y + virtual_screen_height - height; | ||
222 | |||
223 | if (!sIsClassRegistered) | ||
224 | { | ||
225 | // Force redraw when resized and create a private device context | ||
226 | |||
227 | // Makes double click messages. | ||
228 | wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; | ||
229 | |||
230 | // Set message handler function | ||
231 | wc.lpfnWndProc = (WNDPROC) mainWindowProc; | ||
232 | |||
233 | // unused | ||
234 | wc.cbClsExtra = 0; | ||
235 | wc.cbWndExtra = 0; | ||
236 | |||
237 | wc.hInstance = mhInstance; | ||
238 | wc.hIcon = LoadIcon(mhInstance, mIconResource); | ||
239 | |||
240 | // We will set the cursor ourselves | ||
241 | wc.hCursor = NULL; | ||
242 | |||
243 | // background color is not used | ||
244 | if (clearBg) | ||
245 | { | ||
246 | wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); | ||
247 | } | ||
248 | else | ||
249 | { | ||
250 | wc.hbrBackground = (HBRUSH) NULL; | ||
251 | } | ||
252 | |||
253 | // we don't use windows menus | ||
254 | wc.lpszMenuName = NULL; | ||
255 | |||
256 | wc.lpszClassName = mWindowClassName; | ||
257 | |||
258 | if (!RegisterClass(&wc)) | ||
259 | { | ||
260 | OSMessageBox("RegisterClass failed", "Error", OSMB_OK); | ||
261 | return; | ||
262 | } | ||
263 | sIsClassRegistered = TRUE; | ||
264 | } | ||
265 | |||
266 | //----------------------------------------------------------------------- | ||
267 | // Get the current refresh rate | ||
268 | //----------------------------------------------------------------------- | ||
269 | |||
270 | DEVMODE dev_mode; | ||
271 | DWORD current_refresh; | ||
272 | if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) | ||
273 | { | ||
274 | current_refresh = dev_mode.dmDisplayFrequency; | ||
275 | mNativeAspectRatio = ((F32)dev_mode.dmPelsWidth) / ((F32)dev_mode.dmPelsHeight); | ||
276 | } | ||
277 | else | ||
278 | { | ||
279 | current_refresh = 60; | ||
280 | } | ||
281 | |||
282 | //----------------------------------------------------------------------- | ||
283 | // Drop resolution and go fullscreen | ||
284 | // use a display mode with our desired size and depth, with a refresh | ||
285 | // rate as close at possible to the users' default | ||
286 | //----------------------------------------------------------------------- | ||
287 | if (mFullscreen) | ||
288 | { | ||
289 | BOOL success = FALSE; | ||
290 | DWORD closest_refresh = 0; | ||
291 | |||
292 | for (S32 mode_num = 0;; mode_num++) | ||
293 | { | ||
294 | if (!EnumDisplaySettings(NULL, mode_num, &dev_mode)) | ||
295 | { | ||
296 | break; | ||
297 | } | ||
298 | |||
299 | if (dev_mode.dmPelsWidth == width && | ||
300 | dev_mode.dmPelsHeight == height && | ||
301 | dev_mode.dmBitsPerPel == BITS_PER_PIXEL) | ||
302 | { | ||
303 | success = TRUE; | ||
304 | if ((dev_mode.dmDisplayFrequency - current_refresh) | ||
305 | < (closest_refresh - current_refresh)) | ||
306 | { | ||
307 | closest_refresh = dev_mode.dmDisplayFrequency; | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | |||
312 | if (closest_refresh == 0) | ||
313 | { | ||
314 | llwarns << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << llendl; | ||
315 | success = FALSE; | ||
316 | } | ||
317 | |||
318 | // If we found a good resolution, use it. | ||
319 | if (success) | ||
320 | { | ||
321 | success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh); | ||
322 | } | ||
323 | |||
324 | // Keep a copy of the actual current device mode in case we minimize | ||
325 | // and change the screen resolution. JC | ||
326 | EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode); | ||
327 | |||
328 | // If it failed, we don't want to run fullscreen | ||
329 | if (success) | ||
330 | { | ||
331 | mFullscreen = TRUE; | ||
332 | mFullscreenWidth = dev_mode.dmPelsWidth; | ||
333 | mFullscreenHeight = dev_mode.dmPelsHeight; | ||
334 | mFullscreenBits = dev_mode.dmBitsPerPel; | ||
335 | mFullscreenRefresh = dev_mode.dmDisplayFrequency; | ||
336 | |||
337 | llinfos << "Running at " << dev_mode.dmPelsWidth | ||
338 | << "x" << dev_mode.dmPelsHeight | ||
339 | << "x" << dev_mode.dmBitsPerPel | ||
340 | << " @ " << dev_mode.dmDisplayFrequency | ||
341 | << llendl; | ||
342 | } | ||
343 | else | ||
344 | { | ||
345 | mFullscreen = FALSE; | ||
346 | mFullscreenWidth = -1; | ||
347 | mFullscreenHeight = -1; | ||
348 | mFullscreenBits = -1; | ||
349 | mFullscreenRefresh = -1; | ||
350 | |||
351 | char error[256]; | ||
352 | sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height); | ||
353 | OSMessageBox(error, "Error", OSMB_OK); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | //----------------------------------------------------------------------- | ||
358 | // Resize window to account for borders | ||
359 | //----------------------------------------------------------------------- | ||
360 | if (mFullscreen) | ||
361 | { | ||
362 | dw_ex_style = WS_EX_APPWINDOW; | ||
363 | dw_style = WS_POPUP; | ||
364 | |||
365 | // Move window borders out not to cover window contents | ||
366 | AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style); | ||
367 | } | ||
368 | else | ||
369 | { | ||
370 | // Window with an edge | ||
371 | dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; | ||
372 | dw_style = WS_OVERLAPPEDWINDOW; | ||
373 | } | ||
374 | |||
375 | //----------------------------------------------------------------------- | ||
376 | // Create the window | ||
377 | // Microsoft help indicates that GL windows must be created with | ||
378 | // WS_CLIPSIBLINGS and WS_CLIPCHILDREN, but not CS_PARENTDC | ||
379 | //----------------------------------------------------------------------- | ||
380 | mWindowHandle = CreateWindowEx(dw_ex_style, | ||
381 | mWindowClassName, | ||
382 | mWindowTitle, | ||
383 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, | ||
384 | x, // x pos | ||
385 | y, // y pos | ||
386 | window_rect.right - window_rect.left, // width | ||
387 | window_rect.bottom - window_rect.top, // height | ||
388 | NULL, | ||
389 | NULL, | ||
390 | mhInstance, | ||
391 | NULL); | ||
392 | |||
393 | if (!mWindowHandle) | ||
394 | { | ||
395 | DestroyWindow(mWindowHandle); | ||
396 | OSMessageBox("Window creation error", "Error", OSMB_OK); | ||
397 | return; | ||
398 | } | ||
399 | |||
400 | // TODO: add this after resolving _WIN32_WINNT issue | ||
401 | // if (!fullscreen) | ||
402 | // { | ||
403 | // TRACKMOUSEEVENT track_mouse_event; | ||
404 | // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT ); | ||
405 | // track_mouse_event.dwFlags = TME_LEAVE; | ||
406 | // track_mouse_event.hwndTrack = mWindowHandle; | ||
407 | // track_mouse_event.dwHoverTime = HOVER_DEFAULT; | ||
408 | // TrackMouseEvent( &track_mouse_event ); | ||
409 | // } | ||
410 | |||
411 | |||
412 | |||
413 | S32 pfdflags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; | ||
414 | if (use_gl) | ||
415 | { | ||
416 | pfdflags |= PFD_SUPPORT_OPENGL; | ||
417 | } | ||
418 | |||
419 | //----------------------------------------------------------------------- | ||
420 | // Create GL drawing context | ||
421 | //----------------------------------------------------------------------- | ||
422 | PIXELFORMATDESCRIPTOR pfd = | ||
423 | { | ||
424 | sizeof(PIXELFORMATDESCRIPTOR), | ||
425 | 1, | ||
426 | pfdflags, | ||
427 | PFD_TYPE_RGBA, | ||
428 | BITS_PER_PIXEL, | ||
429 | 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused | ||
430 | 8, // alpha bits | ||
431 | 0, // alpha shift | ||
432 | 0, // accum bits | ||
433 | 0, 0, 0, 0, // accum RGBA | ||
434 | 24, // depth bits | ||
435 | 8, // stencil bits, avi added for stencil test | ||
436 | 0, | ||
437 | PFD_MAIN_PLANE, | ||
438 | 0, | ||
439 | 0, 0, 0 | ||
440 | }; | ||
441 | |||
442 | if (!(mhDC = GetDC(mWindowHandle))) | ||
443 | { | ||
444 | close(); | ||
445 | OSMessageBox("Can't make GL device context", "Error", OSMB_OK); | ||
446 | return; | ||
447 | } | ||
448 | |||
449 | if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd))) | ||
450 | { | ||
451 | close(); | ||
452 | OSMessageBox("Can't find suitable pixel format", "Error", OSMB_OK); | ||
453 | return; | ||
454 | } | ||
455 | |||
456 | // Verify what pixel format we actually received. | ||
457 | if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), | ||
458 | &pfd)) | ||
459 | { | ||
460 | close(); | ||
461 | OSMessageBox("Can't get pixel format description", "Error", OSMB_OK); | ||
462 | return; | ||
463 | } | ||
464 | |||
465 | // sanity check pfd returned by Windows | ||
466 | if (!ignore_pixel_depth && (pfd.cColorBits < 32)) | ||
467 | { | ||
468 | close(); | ||
469 | OSMessageBox( | ||
470 | "Second Life requires True Color (32-bit) to run in a window.\n" | ||
471 | "Please go to Control Panels -> Display -> Settings and\n" | ||
472 | "set the screen to 32-bit color.\n" | ||
473 | "Alternately, if you choose to run fullscreen, Second Life\n" | ||
474 | "will automatically adjust the screen each time it runs.", | ||
475 | "Error", | ||
476 | OSMB_OK); | ||
477 | return; | ||
478 | } | ||
479 | |||
480 | if (!ignore_pixel_depth && (pfd.cAlphaBits < 8)) | ||
481 | { | ||
482 | close(); | ||
483 | OSMessageBox( | ||
484 | "Second Life is unable to run because it can't get an 8 bit alpha\n" | ||
485 | "channel. Usually this is due to video card driver issues.\n" | ||
486 | "Please make sure you have the latest video card drivers installed.\n" | ||
487 | "Also be sure your monitor is set to True Color (32-bit) in\n" | ||
488 | "Control Panels -> Display -> Settings.\n" | ||
489 | "If you continue to receive this message, contact customer service.", | ||
490 | "Error", | ||
491 | OSMB_OK); | ||
492 | return; | ||
493 | } | ||
494 | |||
495 | if (!SetPixelFormat(mhDC, pixel_format, &pfd)) | ||
496 | { | ||
497 | close(); | ||
498 | OSMessageBox("Can't set pixel format", "Error", OSMB_OK); | ||
499 | return; | ||
500 | } | ||
501 | |||
502 | if (use_gl) | ||
503 | { | ||
504 | if (!(mhRC = wglCreateContext(mhDC))) | ||
505 | { | ||
506 | close(); | ||
507 | OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK); | ||
508 | return; | ||
509 | } | ||
510 | |||
511 | if (!wglMakeCurrent(mhDC, mhRC)) | ||
512 | { | ||
513 | close(); | ||
514 | OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK); | ||
515 | return; | ||
516 | } | ||
517 | |||
518 | // Check for some explicitly unsupported cards. | ||
519 | const char* RENDERER = (const char*) glGetString(GL_RENDERER); | ||
520 | |||
521 | const char* CARD_LIST[] = | ||
522 | { "RAGE 128", | ||
523 | "RIVA TNT2", | ||
524 | "Intel 810", | ||
525 | "3Dfx/Voodoo3", | ||
526 | "Radeon 7000", | ||
527 | "Radeon 7200", | ||
528 | "Radeon 7500", | ||
529 | "Radeon DDR", | ||
530 | "Radeon VE", | ||
531 | "GDI Generic" }; | ||
532 | const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*); | ||
533 | |||
534 | // Future candidates: | ||
535 | // ProSavage/Twister | ||
536 | // SuperSavage | ||
537 | |||
538 | S32 i; | ||
539 | for (i = 0; i < CARD_COUNT; i++) | ||
540 | { | ||
541 | if (check_for_card(RENDERER, CARD_LIST[i])) | ||
542 | { | ||
543 | close(); | ||
544 | shell_open( "help/unsupported_card.html" ); | ||
545 | return; | ||
546 | } | ||
547 | } | ||
548 | |||
549 | gGLManager.initWGL(); | ||
550 | |||
551 | if (gGLManager.mHasWGLARBPixelFormat && (wglChoosePixelFormatARB != NULL)) | ||
552 | { | ||
553 | // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we | ||
554 | // can get exactly what we want. | ||
555 | GLint attrib_list[256]; | ||
556 | S32 cur_attrib = 0; | ||
557 | |||
558 | attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB; | ||
559 | attrib_list[cur_attrib++] = 24; | ||
560 | |||
561 | attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB; | ||
562 | attrib_list[cur_attrib++] = 8; | ||
563 | |||
564 | attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB; | ||
565 | attrib_list[cur_attrib++] = GL_TRUE; | ||
566 | |||
567 | attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB; | ||
568 | attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB; | ||
569 | |||
570 | attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB; | ||
571 | attrib_list[cur_attrib++] = GL_TRUE; | ||
572 | |||
573 | attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB; | ||
574 | attrib_list[cur_attrib++] = GL_TRUE; | ||
575 | |||
576 | attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB; | ||
577 | attrib_list[cur_attrib++] = 24; | ||
578 | |||
579 | attrib_list[cur_attrib++] = WGL_RED_BITS_ARB; | ||
580 | attrib_list[cur_attrib++] = 8; | ||
581 | |||
582 | attrib_list[cur_attrib++] = WGL_GREEN_BITS_ARB; | ||
583 | attrib_list[cur_attrib++] = 8; | ||
584 | |||
585 | attrib_list[cur_attrib++] = WGL_BLUE_BITS_ARB; | ||
586 | attrib_list[cur_attrib++] = 8; | ||
587 | |||
588 | attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB; | ||
589 | attrib_list[cur_attrib++] = 8; | ||
590 | |||
591 | // End the list | ||
592 | attrib_list[cur_attrib++] = 0; | ||
593 | |||
594 | GLint pixel_formats[256]; | ||
595 | U32 num_formats = 0; | ||
596 | |||
597 | // First we try and get a 32 bit depth pixel format | ||
598 | BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); | ||
599 | if (!result) | ||
600 | { | ||
601 | close(); | ||
602 | show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit"); | ||
603 | return; | ||
604 | } | ||
605 | |||
606 | if (!num_formats) | ||
607 | { | ||
608 | llinfos << "No 32 bit z-buffer, trying 24 bits instead" << llendl; | ||
609 | // Try 24-bit format | ||
610 | attrib_list[1] = 24; | ||
611 | BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); | ||
612 | if (!result) | ||
613 | { | ||
614 | close(); | ||
615 | show_window_creation_error("Error after wglChoosePixelFormatARB 24-bit"); | ||
616 | return; | ||
617 | } | ||
618 | |||
619 | if (!num_formats) | ||
620 | { | ||
621 | llwarns << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << llendl; | ||
622 | attrib_list[1] = 16; | ||
623 | BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); | ||
624 | if (!result || !num_formats) | ||
625 | { | ||
626 | close(); | ||
627 | show_window_creation_error("Error after wglChoosePixelFormatARB 16-bit"); | ||
628 | return; | ||
629 | } | ||
630 | } | ||
631 | |||
632 | llinfos << "Choosing pixel formats: " << num_formats << " pixel formats returned" << llendl; | ||
633 | |||
634 | pixel_format = pixel_formats[0]; | ||
635 | } | ||
636 | |||
637 | DestroyWindow(mWindowHandle); | ||
638 | |||
639 | mWindowHandle = CreateWindowEx(dw_ex_style, | ||
640 | mWindowClassName, | ||
641 | mWindowTitle, | ||
642 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, | ||
643 | x, // x pos | ||
644 | y, // y pos | ||
645 | window_rect.right - window_rect.left, // width | ||
646 | window_rect.bottom - window_rect.top, // height | ||
647 | NULL, | ||
648 | NULL, | ||
649 | mhInstance, | ||
650 | NULL); | ||
651 | |||
652 | if (!(mhDC = GetDC(mWindowHandle))) | ||
653 | { | ||
654 | close(); | ||
655 | OSMessageBox("Can't make GL device context", "Error", OSMB_OK); | ||
656 | return; | ||
657 | } | ||
658 | |||
659 | if (!SetPixelFormat(mhDC, pixel_format, &pfd)) | ||
660 | { | ||
661 | close(); | ||
662 | OSMessageBox("Can't set pixel format", "Error", OSMB_OK); | ||
663 | return; | ||
664 | } | ||
665 | |||
666 | int swap_method = 0; | ||
667 | GLint swap_query = WGL_SWAP_METHOD_ARB; | ||
668 | |||
669 | if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method)) | ||
670 | { | ||
671 | switch (swap_method) | ||
672 | { | ||
673 | case WGL_SWAP_EXCHANGE_ARB: | ||
674 | mSwapMethod = SWAP_METHOD_EXCHANGE; | ||
675 | llinfos << "Swap Method: Exchange" << llendl; | ||
676 | break; | ||
677 | case WGL_SWAP_COPY_ARB: | ||
678 | mSwapMethod = SWAP_METHOD_COPY; | ||
679 | llinfos << "Swap Method: Copy" << llendl; | ||
680 | break; | ||
681 | case WGL_SWAP_UNDEFINED_ARB: | ||
682 | mSwapMethod = SWAP_METHOD_UNDEFINED; | ||
683 | llinfos << "Swap Method: Undefined" << llendl; | ||
684 | break; | ||
685 | default: | ||
686 | mSwapMethod = SWAP_METHOD_UNDEFINED; | ||
687 | llinfos << "Swap Method: Unknown" << llendl; | ||
688 | break; | ||
689 | } | ||
690 | } | ||
691 | } | ||
692 | else | ||
693 | { | ||
694 | llwarns << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat!" << llendl; | ||
695 | } | ||
696 | |||
697 | // Verify what pixel format we actually received. | ||
698 | if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), | ||
699 | &pfd)) | ||
700 | { | ||
701 | close(); | ||
702 | OSMessageBox("Can't get pixel format description", "Error", OSMB_OK); | ||
703 | return; | ||
704 | } | ||
705 | llinfos << "GL buffer: Color Bits " << S32(pfd.cColorBits) | ||
706 | << " Alpha Bits " << S32(pfd.cAlphaBits) | ||
707 | << " Depth Bits " << S32(pfd.cDepthBits) | ||
708 | << llendl; | ||
709 | |||
710 | if (pfd.cColorBits < 32) | ||
711 | { | ||
712 | close(); | ||
713 | OSMessageBox( | ||
714 | "Second Life requires True Color (32-bit) to run in a window.\n" | ||
715 | "Please go to Control Panels -> Display -> Settings and\n" | ||
716 | "set the screen to 32-bit color.\n" | ||
717 | "Alternately, if you choose to run fullscreen, Second Life\n" | ||
718 | "will automatically adjust the screen each time it runs.", | ||
719 | "Error", | ||
720 | OSMB_OK); | ||
721 | return; | ||
722 | } | ||
723 | |||
724 | if (pfd.cAlphaBits < 8) | ||
725 | { | ||
726 | close(); | ||
727 | OSMessageBox( | ||
728 | "Second Life is unable to run because it can't get an 8 bit alpha\n" | ||
729 | "channel. Usually this is due to video card driver issues.\n" | ||
730 | "Please make sure you have the latest video card drivers installed.\n" | ||
731 | "Also be sure your monitor is set to True Color (32-bit) in\n" | ||
732 | "Control Panels -> Display -> Settings.\n" | ||
733 | "If you continue to receive this message, contact customer service.", | ||
734 | "Error", | ||
735 | OSMB_OK); | ||
736 | return; | ||
737 | } | ||
738 | |||
739 | if (!(mhRC = wglCreateContext(mhDC))) | ||
740 | { | ||
741 | close(); | ||
742 | OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK); | ||
743 | return; | ||
744 | } | ||
745 | |||
746 | if (!wglMakeCurrent(mhDC, mhRC)) | ||
747 | { | ||
748 | close(); | ||
749 | OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK); | ||
750 | return; | ||
751 | } | ||
752 | |||
753 | if (!gGLManager.initGL()) | ||
754 | { | ||
755 | close(); | ||
756 | OSMessageBox( | ||
757 | "Second Life is unable to run because your video card drivers\n" | ||
758 | "are out of date or unsupported. Please make sure you have\n" | ||
759 | "the latest video card drivers installed.\n\n" | ||
760 | "If you continue to receive this message, contact customer service.", | ||
761 | "Error", | ||
762 | OSMB_OK); | ||
763 | return; | ||
764 | } | ||
765 | |||
766 | // Disable vertical sync for swap | ||
767 | if (disable_vsync && wglSwapIntervalEXT) | ||
768 | { | ||
769 | llinfos << "Disabling vertical sync" << llendl; | ||
770 | wglSwapIntervalEXT(0); | ||
771 | } | ||
772 | else | ||
773 | { | ||
774 | llinfos << "Keeping vertical sync" << llendl; | ||
775 | } | ||
776 | |||
777 | |||
778 | // OK, let's get the current gamma information and store it off. | ||
779 | mCurrentGamma = 0.f; // Not set, default; | ||
780 | if (!GetDeviceGammaRamp(mhDC, mPrevGammaRamp)) | ||
781 | { | ||
782 | llwarns << "Unable to get device gamma ramp" << llendl; | ||
783 | } | ||
784 | |||
785 | // Calculate what the current gamma is. From a posting by Garrett T. Bass, Get/SetDeviceGammaRamp Demystified | ||
786 | // http://apollo.iwt.uni-bielefeld.de/~ml_robot/OpenGL-04-2000/0058.html | ||
787 | |||
788 | // We're going to assume that gamma's the same for all 3 channels, because I don't feel like doing it otherwise. | ||
789 | // Using the red channel. | ||
790 | |||
791 | F32 Csum = 0.0; | ||
792 | S32 Ccount = 0; | ||
793 | for (i = 0; i < 256; i++) | ||
794 | { | ||
795 | if (i != 0 && mPrevGammaRamp[i] != 0 && mPrevGammaRamp[i] != 65536) | ||
796 | { | ||
797 | F64 B = (i % 256) / 256.0; | ||
798 | F64 A = mPrevGammaRamp[i] / 65536.0; | ||
799 | F32 C = (F32) ( log(A) / log(B) ); | ||
800 | Csum += C; | ||
801 | Ccount++; | ||
802 | } | ||
803 | } | ||
804 | mCurrentGamma = Csum / Ccount; | ||
805 | |||
806 | llinfos << "Previous gamma: " << mCurrentGamma << llendl; | ||
807 | } | ||
808 | |||
809 | |||
810 | //store this pointer for wndProc callback | ||
811 | SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)this); | ||
812 | |||
813 | //start with arrow cursor | ||
814 | initCursors(); | ||
815 | setCursor( UI_CURSOR_ARROW ); | ||
816 | |||
817 | // Direct Input | ||
818 | HRESULT hr; | ||
819 | |||
820 | if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, | ||
821 | IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) ) | ||
822 | { | ||
823 | llwarns << "Direct8InputCreate failed!" << llendl; | ||
824 | } | ||
825 | else | ||
826 | { | ||
827 | while(1) | ||
828 | { | ||
829 | // Look for a simple joystick we can use for this sample program. | ||
830 | if (FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, | ||
831 | EnumJoysticksCallback, | ||
832 | NULL, DIEDFL_ATTACHEDONLY ) ) ) | ||
833 | break; | ||
834 | if (!g_pJoystick) | ||
835 | break; | ||
836 | if( FAILED( hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick ) ) ) | ||
837 | break; | ||
838 | if( FAILED( hr = g_pJoystick->EnumObjects( EnumObjectsCallback, | ||
839 | (VOID*)mWindowHandle, DIDFT_ALL ) ) ) | ||
840 | break; | ||
841 | g_pJoystick->Acquire(); | ||
842 | break; | ||
843 | } | ||
844 | } | ||
845 | |||
846 | SetTimer( mWindowHandle, 0, 1000 / 30, NULL ); // 30 fps timer | ||
847 | mJoyStickState = 0; | ||
848 | mJoyButtonState = 0; | ||
849 | } | ||
850 | |||
851 | |||
852 | LLWindowWin32::~LLWindowWin32() | ||
853 | { | ||
854 | delete [] mWindowTitle; | ||
855 | mWindowTitle = NULL; | ||
856 | |||
857 | delete [] mSupportedResolutions; | ||
858 | mSupportedResolutions = NULL; | ||
859 | |||
860 | delete mWindowClassName; | ||
861 | mWindowClassName = NULL; | ||
862 | } | ||
863 | |||
864 | void LLWindowWin32::show() | ||
865 | { | ||
866 | ShowWindow(mWindowHandle, SW_SHOW); | ||
867 | SetForegroundWindow(mWindowHandle); | ||
868 | SetFocus(mWindowHandle); | ||
869 | } | ||
870 | |||
871 | void LLWindowWin32::hide() | ||
872 | { | ||
873 | setMouseClipping(FALSE); | ||
874 | ShowWindow(mWindowHandle, SW_HIDE); | ||
875 | } | ||
876 | |||
877 | void LLWindowWin32::minimize() | ||
878 | { | ||
879 | setMouseClipping(FALSE); | ||
880 | showCursor(); | ||
881 | ShowWindow(mWindowHandle, SW_MINIMIZE); | ||
882 | } | ||
883 | |||
884 | |||
885 | void LLWindowWin32::restore() | ||
886 | { | ||
887 | ShowWindow(mWindowHandle, SW_RESTORE); | ||
888 | SetForegroundWindow(mWindowHandle); | ||
889 | SetFocus(mWindowHandle); | ||
890 | } | ||
891 | |||
892 | |||
893 | // close() destroys all OS-specific code associated with a window. | ||
894 | // Usually called from LLWindowManager::destroyWindow() | ||
895 | void LLWindowWin32::close() | ||
896 | { | ||
897 | llinfos << "Closing LLWindowWin32" << llendl; | ||
898 | // Is window is already closed? | ||
899 | if (!mWindowHandle) | ||
900 | { | ||
901 | return; | ||
902 | } | ||
903 | |||
904 | // Make sure cursor is visible and we haven't mangled the clipping state. | ||
905 | setMouseClipping(FALSE); | ||
906 | showCursor(); | ||
907 | |||
908 | // Go back to screen mode written in the registry. | ||
909 | if (mFullscreen) | ||
910 | { | ||
911 | resetDisplayResolution(); | ||
912 | } | ||
913 | |||
914 | // Clean up remaining GL state | ||
915 | llinfos << "Shutting down GL" << llendl; | ||
916 | gGLManager.shutdownGL(); | ||
917 | |||
918 | llinfos << "Releasing Context" << llendl; | ||
919 | if (mhRC) | ||
920 | { | ||
921 | if (!wglMakeCurrent(NULL, NULL)) | ||
922 | { | ||
923 | llwarns << "Release of DC and RC failed" << llendl; | ||
924 | } | ||
925 | |||
926 | if (!wglDeleteContext(mhRC)) | ||
927 | { | ||
928 | llwarns << "Release of rendering context failed" << llendl; | ||
929 | } | ||
930 | |||
931 | mhRC = NULL; | ||
932 | } | ||
933 | |||
934 | // Restore gamma to the system values. | ||
935 | restoreGamma(); | ||
936 | |||
937 | if (mhDC && !ReleaseDC(mWindowHandle, mhDC)) | ||
938 | { | ||
939 | llwarns << "Release of ghDC failed" << llendl; | ||
940 | mhDC = NULL; | ||
941 | } | ||
942 | |||
943 | llinfos << "Destroying Window" << llendl; | ||
944 | |||
945 | // Don't process events in our mainWindowProc any longer. | ||
946 | SetWindowLong(mWindowHandle, GWL_USERDATA, NULL); | ||
947 | |||
948 | // Make sure we don't leave a blank toolbar button. | ||
949 | ShowWindow(mWindowHandle, SW_HIDE); | ||
950 | |||
951 | // This causes WM_DESTROY to be sent *immediately* | ||
952 | if (!DestroyWindow(mWindowHandle)) | ||
953 | { | ||
954 | OSMessageBox("DestroyWindow(mWindowHandle) failed", "Shutdown Error", OSMB_OK); | ||
955 | } | ||
956 | |||
957 | mWindowHandle = NULL; | ||
958 | } | ||
959 | |||
960 | BOOL LLWindowWin32::isValid() | ||
961 | { | ||
962 | return (mWindowHandle != NULL); | ||
963 | } | ||
964 | |||
965 | BOOL LLWindowWin32::getVisible() | ||
966 | { | ||
967 | return (mWindowHandle && IsWindowVisible(mWindowHandle)); | ||
968 | } | ||
969 | |||
970 | BOOL LLWindowWin32::getMinimized() | ||
971 | { | ||
972 | return (mWindowHandle && IsIconic(mWindowHandle)); | ||
973 | } | ||
974 | |||
975 | BOOL LLWindowWin32::getMaximized() | ||
976 | { | ||
977 | return (mWindowHandle && IsZoomed(mWindowHandle)); | ||
978 | } | ||
979 | |||
980 | BOOL LLWindowWin32::maximize() | ||
981 | { | ||
982 | BOOL success = FALSE; | ||
983 | if (!mWindowHandle) return success; | ||
984 | |||
985 | WINDOWPLACEMENT placement; | ||
986 | placement.length = sizeof(WINDOWPLACEMENT); | ||
987 | |||
988 | success = GetWindowPlacement(mWindowHandle, &placement); | ||
989 | if (!success) return success; | ||
990 | |||
991 | placement.showCmd = SW_MAXIMIZE; | ||
992 | |||
993 | success = SetWindowPlacement(mWindowHandle, &placement); | ||
994 | return success; | ||
995 | } | ||
996 | |||
997 | BOOL LLWindowWin32::getFullscreen() | ||
998 | { | ||
999 | return mFullscreen; | ||
1000 | } | ||
1001 | |||
1002 | BOOL LLWindowWin32::getPosition(LLCoordScreen *position) | ||
1003 | { | ||
1004 | RECT window_rect; | ||
1005 | |||
1006 | if (!mWindowHandle || | ||
1007 | !GetWindowRect(mWindowHandle, &window_rect) || | ||
1008 | NULL == position) | ||
1009 | { | ||
1010 | return FALSE; | ||
1011 | } | ||
1012 | |||
1013 | position->mX = window_rect.left; | ||
1014 | position->mY = window_rect.top; | ||
1015 | return TRUE; | ||
1016 | } | ||
1017 | |||
1018 | BOOL LLWindowWin32::getSize(LLCoordScreen *size) | ||
1019 | { | ||
1020 | RECT window_rect; | ||
1021 | |||
1022 | if (!mWindowHandle || | ||
1023 | !GetWindowRect(mWindowHandle, &window_rect) || | ||
1024 | NULL == size) | ||
1025 | { | ||
1026 | return FALSE; | ||
1027 | } | ||
1028 | |||
1029 | size->mX = window_rect.right - window_rect.left; | ||
1030 | size->mY = window_rect.bottom - window_rect.top; | ||
1031 | return TRUE; | ||
1032 | } | ||
1033 | |||
1034 | BOOL LLWindowWin32::getSize(LLCoordWindow *size) | ||
1035 | { | ||
1036 | RECT client_rect; | ||
1037 | |||
1038 | if (!mWindowHandle || | ||
1039 | !GetClientRect(mWindowHandle, &client_rect) || | ||
1040 | NULL == size) | ||
1041 | { | ||
1042 | return FALSE; | ||
1043 | } | ||
1044 | |||
1045 | size->mX = client_rect.right - client_rect.left; | ||
1046 | size->mY = client_rect.bottom - client_rect.top; | ||
1047 | return TRUE; | ||
1048 | } | ||
1049 | |||
1050 | BOOL LLWindowWin32::setPosition(const LLCoordScreen position) | ||
1051 | { | ||
1052 | LLCoordScreen size; | ||
1053 | |||
1054 | if (!mWindowHandle) | ||
1055 | { | ||
1056 | return FALSE; | ||
1057 | } | ||
1058 | getSize(&size); | ||
1059 | moveWindow(position, size); | ||
1060 | return TRUE; | ||
1061 | } | ||
1062 | |||
1063 | BOOL LLWindowWin32::setSize(const LLCoordScreen size) | ||
1064 | { | ||
1065 | LLCoordScreen position; | ||
1066 | |||
1067 | getPosition(&position); | ||
1068 | if (!mWindowHandle) | ||
1069 | { | ||
1070 | return FALSE; | ||
1071 | } | ||
1072 | |||
1073 | moveWindow(position, size); | ||
1074 | return TRUE; | ||
1075 | } | ||
1076 | |||
1077 | // changing fullscreen resolution | ||
1078 | BOOL LLWindowWin32::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync) | ||
1079 | { | ||
1080 | GLuint pixel_format; | ||
1081 | DEVMODE dev_mode; | ||
1082 | DWORD current_refresh; | ||
1083 | DWORD dw_ex_style; | ||
1084 | DWORD dw_style; | ||
1085 | RECT window_rect; | ||
1086 | S32 width = size.mX; | ||
1087 | S32 height = size.mY; | ||
1088 | |||
1089 | resetDisplayResolution(); | ||
1090 | |||
1091 | if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) | ||
1092 | { | ||
1093 | current_refresh = dev_mode.dmDisplayFrequency; | ||
1094 | } | ||
1095 | else | ||
1096 | { | ||
1097 | current_refresh = 60; | ||
1098 | } | ||
1099 | |||
1100 | gGLManager.shutdownGL(); | ||
1101 | //destroy gl context | ||
1102 | if (mhRC) | ||
1103 | { | ||
1104 | if (!wglMakeCurrent(NULL, NULL)) | ||
1105 | { | ||
1106 | llwarns << "Release of DC and RC failed" << llendl; | ||
1107 | } | ||
1108 | |||
1109 | if (!wglDeleteContext(mhRC)) | ||
1110 | { | ||
1111 | llwarns << "Release of rendering context failed" << llendl; | ||
1112 | } | ||
1113 | |||
1114 | mhRC = NULL; | ||
1115 | } | ||
1116 | |||
1117 | if (fullscreen) | ||
1118 | { | ||
1119 | mFullscreen = TRUE; | ||
1120 | BOOL success = FALSE; | ||
1121 | DWORD closest_refresh = 0; | ||
1122 | |||
1123 | for (S32 mode_num = 0;; mode_num++) | ||
1124 | { | ||
1125 | if (!EnumDisplaySettings(NULL, mode_num, &dev_mode)) | ||
1126 | { | ||
1127 | break; | ||
1128 | } | ||
1129 | |||
1130 | if (dev_mode.dmPelsWidth == width && | ||
1131 | dev_mode.dmPelsHeight == height && | ||
1132 | dev_mode.dmBitsPerPel == BITS_PER_PIXEL) | ||
1133 | { | ||
1134 | success = TRUE; | ||
1135 | if ((dev_mode.dmDisplayFrequency - current_refresh) | ||
1136 | < (closest_refresh - current_refresh)) | ||
1137 | { | ||
1138 | closest_refresh = dev_mode.dmDisplayFrequency; | ||
1139 | } | ||
1140 | } | ||
1141 | } | ||
1142 | |||
1143 | if (closest_refresh == 0) | ||
1144 | { | ||
1145 | llwarns << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << llendl; | ||
1146 | return FALSE; | ||
1147 | } | ||
1148 | |||
1149 | // If we found a good resolution, use it. | ||
1150 | if (success) | ||
1151 | { | ||
1152 | success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh); | ||
1153 | } | ||
1154 | |||
1155 | // Keep a copy of the actual current device mode in case we minimize | ||
1156 | // and change the screen resolution. JC | ||
1157 | EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode); | ||
1158 | |||
1159 | if (success) | ||
1160 | { | ||
1161 | mFullscreen = TRUE; | ||
1162 | mFullscreenWidth = dev_mode.dmPelsWidth; | ||
1163 | mFullscreenHeight = dev_mode.dmPelsHeight; | ||
1164 | mFullscreenBits = dev_mode.dmBitsPerPel; | ||
1165 | mFullscreenRefresh = dev_mode.dmDisplayFrequency; | ||
1166 | |||
1167 | llinfos << "Running at " << dev_mode.dmPelsWidth | ||
1168 | << "x" << dev_mode.dmPelsHeight | ||
1169 | << "x" << dev_mode.dmBitsPerPel | ||
1170 | << " @ " << dev_mode.dmDisplayFrequency | ||
1171 | << llendl; | ||
1172 | |||
1173 | window_rect.left = (long) 0; | ||
1174 | window_rect.right = (long) width; // Windows GDI rects don't include rightmost pixel | ||
1175 | window_rect.top = (long) 0; | ||
1176 | window_rect.bottom = (long) height; | ||
1177 | dw_ex_style = WS_EX_APPWINDOW; | ||
1178 | dw_style = WS_POPUP; | ||
1179 | |||
1180 | // Move window borders out not to cover window contents | ||
1181 | AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style); | ||
1182 | } | ||
1183 | // If it failed, we don't want to run fullscreen | ||
1184 | else | ||
1185 | { | ||
1186 | mFullscreen = FALSE; | ||
1187 | mFullscreenWidth = -1; | ||
1188 | mFullscreenHeight = -1; | ||
1189 | mFullscreenBits = -1; | ||
1190 | mFullscreenRefresh = -1; | ||
1191 | |||
1192 | llinfos << "Unable to run fullscreen at " << width << "x" << height << llendl; | ||
1193 | llinfos << "Running in window." << llendl; | ||
1194 | return FALSE; | ||
1195 | } | ||
1196 | } | ||
1197 | else | ||
1198 | { | ||
1199 | mFullscreen = FALSE; | ||
1200 | window_rect.left = (long) 0; | ||
1201 | window_rect.right = (long) width; // Windows GDI rects don't include rightmost pixel | ||
1202 | window_rect.top = (long) 0; | ||
1203 | window_rect.bottom = (long) height; | ||
1204 | // Window with an edge | ||
1205 | dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; | ||
1206 | dw_style = WS_OVERLAPPEDWINDOW; | ||
1207 | } | ||
1208 | |||
1209 | // don't post quit messages when destroying old windows | ||
1210 | mPostQuit = FALSE; | ||
1211 | |||
1212 | // create window | ||
1213 | DestroyWindow(mWindowHandle); | ||
1214 | mWindowHandle = CreateWindowEx(dw_ex_style, | ||
1215 | mWindowClassName, | ||
1216 | mWindowTitle, | ||
1217 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, | ||
1218 | window_rect.left, // x pos | ||
1219 | window_rect.top, // y pos | ||
1220 | window_rect.right - window_rect.left, // width | ||
1221 | window_rect.bottom - window_rect.top, // height | ||
1222 | NULL, | ||
1223 | NULL, | ||
1224 | mhInstance, | ||
1225 | NULL); | ||
1226 | |||
1227 | //----------------------------------------------------------------------- | ||
1228 | // Create GL drawing context | ||
1229 | //----------------------------------------------------------------------- | ||
1230 | static PIXELFORMATDESCRIPTOR pfd = | ||
1231 | { | ||
1232 | sizeof(PIXELFORMATDESCRIPTOR), | ||
1233 | 1, | ||
1234 | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, | ||
1235 | PFD_TYPE_RGBA, | ||
1236 | BITS_PER_PIXEL, | ||
1237 | 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused | ||
1238 | 8, // alpha bits | ||
1239 | 0, // alpha shift | ||
1240 | 0, // accum bits | ||
1241 | 0, 0, 0, 0, // accum RGBA | ||
1242 | 24, // depth bits | ||
1243 | 8, // stencil bits, avi added for stencil test | ||
1244 | 0, | ||
1245 | PFD_MAIN_PLANE, | ||
1246 | 0, | ||
1247 | 0, 0, 0 | ||
1248 | }; | ||
1249 | |||
1250 | if (!(mhDC = GetDC(mWindowHandle))) | ||
1251 | { | ||
1252 | close(); | ||
1253 | OSMessageBox("Can't make GL device context", "Error", OSMB_OK); | ||
1254 | return FALSE; | ||
1255 | } | ||
1256 | |||
1257 | if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd))) | ||
1258 | { | ||
1259 | close(); | ||
1260 | OSMessageBox("Can't find suitable pixel format", "Error", OSMB_OK); | ||
1261 | return FALSE; | ||
1262 | } | ||
1263 | |||
1264 | // Verify what pixel format we actually received. | ||
1265 | if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), | ||
1266 | &pfd)) | ||
1267 | { | ||
1268 | close(); | ||
1269 | OSMessageBox("Can't get pixel format description", "Error", OSMB_OK); | ||
1270 | return FALSE; | ||
1271 | } | ||
1272 | |||
1273 | if (pfd.cColorBits < 32) | ||
1274 | { | ||
1275 | close(); | ||
1276 | OSMessageBox( | ||
1277 | "Second Life requires True Color (32-bit) to run in a window.\n" | ||
1278 | "Please go to Control Panels -> Display -> Settings and\n" | ||
1279 | "set the screen to 32-bit color.\n" | ||
1280 | "Alternately, if you choose to run fullscreen, Second Life\n" | ||
1281 | "will automatically adjust the screen each time it runs.", | ||
1282 | "Error", | ||
1283 | OSMB_OK); | ||
1284 | return FALSE; | ||
1285 | } | ||
1286 | |||
1287 | if (pfd.cAlphaBits < 8) | ||
1288 | { | ||
1289 | close(); | ||
1290 | OSMessageBox( | ||
1291 | "Second Life is unable to run because it can't get an 8 bit alpha\n" | ||
1292 | "channel. Usually this is due to video card driver issues.\n" | ||
1293 | "Please make sure you have the latest video card drivers installed.\n" | ||
1294 | "Also be sure your monitor is set to True Color (32-bit) in\n" | ||
1295 | "Control Panels -> Display -> Settings.\n" | ||
1296 | "If you continue to receive this message, contact customer service.", | ||
1297 | "Error", | ||
1298 | OSMB_OK); | ||
1299 | return FALSE; | ||
1300 | } | ||
1301 | |||
1302 | if (!SetPixelFormat(mhDC, pixel_format, &pfd)) | ||
1303 | { | ||
1304 | close(); | ||
1305 | OSMessageBox("Can't set pixel format", "Error", OSMB_OK); | ||
1306 | return FALSE; | ||
1307 | } | ||
1308 | |||
1309 | if (!(mhRC = wglCreateContext(mhDC))) | ||
1310 | { | ||
1311 | close(); | ||
1312 | OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK); | ||
1313 | return FALSE; | ||
1314 | } | ||
1315 | |||
1316 | if (!wglMakeCurrent(mhDC, mhRC)) | ||
1317 | { | ||
1318 | close(); | ||
1319 | OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK); | ||
1320 | return FALSE; | ||
1321 | } | ||
1322 | |||
1323 | gGLManager.initWGL(); | ||
1324 | |||
1325 | if (wglChoosePixelFormatARB) | ||
1326 | { | ||
1327 | // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we | ||
1328 | // can get exactly what we want. | ||
1329 | GLint attrib_list[256]; | ||
1330 | S32 cur_attrib = 0; | ||
1331 | |||
1332 | attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB; | ||
1333 | attrib_list[cur_attrib++] = 24; | ||
1334 | |||
1335 | attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB; | ||
1336 | attrib_list[cur_attrib++] = 8; | ||
1337 | |||
1338 | attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB; | ||
1339 | attrib_list[cur_attrib++] = GL_TRUE; | ||
1340 | |||
1341 | attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB; | ||
1342 | attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB; | ||
1343 | |||
1344 | attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB; | ||
1345 | attrib_list[cur_attrib++] = GL_TRUE; | ||
1346 | |||
1347 | attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB; | ||
1348 | attrib_list[cur_attrib++] = GL_TRUE; | ||
1349 | |||
1350 | attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB; | ||
1351 | attrib_list[cur_attrib++] = 24; | ||
1352 | |||
1353 | attrib_list[cur_attrib++] = WGL_RED_BITS_ARB; | ||
1354 | attrib_list[cur_attrib++] = 8; | ||
1355 | |||
1356 | attrib_list[cur_attrib++] = WGL_GREEN_BITS_ARB; | ||
1357 | attrib_list[cur_attrib++] = 8; | ||
1358 | |||
1359 | attrib_list[cur_attrib++] = WGL_BLUE_BITS_ARB; | ||
1360 | attrib_list[cur_attrib++] = 8; | ||
1361 | |||
1362 | attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB; | ||
1363 | attrib_list[cur_attrib++] = 8; | ||
1364 | |||
1365 | // End the list | ||
1366 | attrib_list[cur_attrib++] = 0; | ||
1367 | |||
1368 | GLint pixel_formats[256]; | ||
1369 | U32 num_formats = 0; | ||
1370 | |||
1371 | // First we try and get a 32 bit depth pixel format | ||
1372 | BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); | ||
1373 | if (!result) | ||
1374 | { | ||
1375 | close(); | ||
1376 | show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit"); | ||
1377 | return FALSE; | ||
1378 | } | ||
1379 | |||
1380 | if (!num_formats) | ||
1381 | { | ||
1382 | llinfos << "No 32 bit z-buffer, trying 24 bits instead" << llendl; | ||
1383 | // Try 24-bit format | ||
1384 | attrib_list[1] = 24; | ||
1385 | BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); | ||
1386 | if (!result) | ||
1387 | { | ||
1388 | close(); | ||
1389 | show_window_creation_error("Error after wglChoosePixelFormatARB 24-bit"); | ||
1390 | return FALSE; | ||
1391 | } | ||
1392 | |||
1393 | if (!num_formats) | ||
1394 | { | ||
1395 | llwarns << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << llendl; | ||
1396 | attrib_list[1] = 16; | ||
1397 | BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); | ||
1398 | if (!result || !num_formats) | ||
1399 | { | ||
1400 | close(); | ||
1401 | show_window_creation_error("Error after wglChoosePixelFormatARB 16-bit"); | ||
1402 | return FALSE; | ||
1403 | } | ||
1404 | } | ||
1405 | |||
1406 | llinfos << "Choosing pixel formats: " << num_formats << " pixel formats returned" << llendl; | ||
1407 | |||
1408 | pixel_format = pixel_formats[0]; | ||
1409 | } | ||
1410 | |||
1411 | DestroyWindow(mWindowHandle); | ||
1412 | mWindowHandle = CreateWindowEx(dw_ex_style, | ||
1413 | mWindowClassName, | ||
1414 | mWindowTitle, | ||
1415 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style, | ||
1416 | window_rect.left, // x pos | ||
1417 | window_rect.top, // y pos | ||
1418 | window_rect.right - window_rect.left, // width | ||
1419 | window_rect.bottom - window_rect.top, // height | ||
1420 | NULL, | ||
1421 | NULL, | ||
1422 | mhInstance, | ||
1423 | NULL); | ||
1424 | |||
1425 | if (!(mhDC = GetDC(mWindowHandle))) | ||
1426 | { | ||
1427 | close(); | ||
1428 | OSMessageBox("Can't make GL device context", "Error", OSMB_OK); | ||
1429 | return FALSE; | ||
1430 | } | ||
1431 | |||
1432 | if (!SetPixelFormat(mhDC, pixel_format, &pfd)) | ||
1433 | { | ||
1434 | close(); | ||
1435 | OSMessageBox("Can't set pixel format", "Error", OSMB_OK); | ||
1436 | return FALSE; | ||
1437 | } | ||
1438 | |||
1439 | int swap_method = 0; | ||
1440 | GLint swap_query = WGL_SWAP_METHOD_ARB; | ||
1441 | |||
1442 | if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method)) | ||
1443 | { | ||
1444 | switch (swap_method) | ||
1445 | { | ||
1446 | case WGL_SWAP_EXCHANGE_ARB: | ||
1447 | mSwapMethod = SWAP_METHOD_EXCHANGE; | ||
1448 | llinfos << "Swap Method: Exchange" << llendl; | ||
1449 | break; | ||
1450 | case WGL_SWAP_COPY_ARB: | ||
1451 | mSwapMethod = SWAP_METHOD_COPY; | ||
1452 | llinfos << "Swap Method: Copy" << llendl; | ||
1453 | break; | ||
1454 | case WGL_SWAP_UNDEFINED_ARB: | ||
1455 | mSwapMethod = SWAP_METHOD_UNDEFINED; | ||
1456 | llinfos << "Swap Method: Undefined" << llendl; | ||
1457 | break; | ||
1458 | default: | ||
1459 | mSwapMethod = SWAP_METHOD_UNDEFINED; | ||
1460 | llinfos << "Swap Method: Unknown" << llendl; | ||
1461 | break; | ||
1462 | } | ||
1463 | } | ||
1464 | } | ||
1465 | else | ||
1466 | { | ||
1467 | llwarns << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat!" << llendl; | ||
1468 | } | ||
1469 | |||
1470 | // Verify what pixel format we actually received. | ||
1471 | if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), | ||
1472 | &pfd)) | ||
1473 | { | ||
1474 | close(); | ||
1475 | OSMessageBox("Can't get pixel format description", "Error", OSMB_OK); | ||
1476 | return FALSE; | ||
1477 | } | ||
1478 | |||
1479 | llinfos << "GL buffer: Color Bits " << S32(pfd.cColorBits) | ||
1480 | << " Alpha Bits " << S32(pfd.cAlphaBits) | ||
1481 | << " Depth Bits " << S32(pfd.cDepthBits) | ||
1482 | << llendl; | ||
1483 | |||
1484 | if (pfd.cColorBits < 32) | ||
1485 | { | ||
1486 | close(); | ||
1487 | OSMessageBox( | ||
1488 | "Second Life requires True Color (32-bit) to run in a window.\n" | ||
1489 | "Please go to Control Panels -> Display -> Settings and\n" | ||
1490 | "set the screen to 32-bit color.\n" | ||
1491 | "Alternately, if you choose to run fullscreen, Second Life\n" | ||
1492 | "will automatically adjust the screen each time it runs.", | ||
1493 | "Error", | ||
1494 | OSMB_OK); | ||
1495 | return FALSE; | ||
1496 | } | ||
1497 | |||
1498 | if (pfd.cAlphaBits < 8) | ||
1499 | { | ||
1500 | close(); | ||
1501 | OSMessageBox( | ||
1502 | "Second Life is unable to run because it can't get an 8 bit alpha\n" | ||
1503 | "channel. Usually this is due to video card driver issues.\n" | ||
1504 | "Please make sure you have the latest video card drivers installed.\n" | ||
1505 | "Also be sure your monitor is set to True Color (32-bit) in\n" | ||
1506 | "Control Panels -> Display -> Settings.\n" | ||
1507 | "If you continue to receive this message, contact customer service.", | ||
1508 | "Error", | ||
1509 | OSMB_OK); | ||
1510 | return FALSE; | ||
1511 | } | ||
1512 | |||
1513 | if (!(mhRC = wglCreateContext(mhDC))) | ||
1514 | { | ||
1515 | close(); | ||
1516 | OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK); | ||
1517 | return FALSE; | ||
1518 | } | ||
1519 | |||
1520 | if (!wglMakeCurrent(mhDC, mhRC)) | ||
1521 | { | ||
1522 | close(); | ||
1523 | OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK); | ||
1524 | return FALSE; | ||
1525 | } | ||
1526 | |||
1527 | if (!gGLManager.initGL()) | ||
1528 | { | ||
1529 | close(); | ||
1530 | OSMessageBox( | ||
1531 | "Second Life is unable to run because your video card drivers\n" | ||
1532 | "are out of date or unsupported. Please make sure you have\n" | ||
1533 | "the latest video card drivers installed.\n\n" | ||
1534 | "If you continue to receive this message, contact customer service.", | ||
1535 | "Error", | ||
1536 | OSMB_OK); | ||
1537 | return FALSE; | ||
1538 | } | ||
1539 | |||
1540 | // Disable vertical sync for swap | ||
1541 | if (disable_vsync && wglSwapIntervalEXT) | ||
1542 | { | ||
1543 | llinfos << "Disabling vertical sync" << llendl; | ||
1544 | wglSwapIntervalEXT(0); | ||
1545 | } | ||
1546 | else | ||
1547 | { | ||
1548 | llinfos << "Keeping vertical sync" << llendl; | ||
1549 | } | ||
1550 | |||
1551 | SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)this); | ||
1552 | show(); | ||
1553 | |||
1554 | // ok to post quit messages now | ||
1555 | mPostQuit = TRUE; | ||
1556 | return TRUE; | ||
1557 | } | ||
1558 | |||
1559 | void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScreen& size ) | ||
1560 | { | ||
1561 | if( mIsMouseClipping ) | ||
1562 | { | ||
1563 | RECT client_rect_in_screen_space; | ||
1564 | if( getClientRectInScreenSpace( &client_rect_in_screen_space ) ) | ||
1565 | { | ||
1566 | ClipCursor( &client_rect_in_screen_space ); | ||
1567 | } | ||
1568 | } | ||
1569 | |||
1570 | MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY, TRUE); | ||
1571 | } | ||
1572 | |||
1573 | BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position) | ||
1574 | { | ||
1575 | LLCoordScreen screen_pos; | ||
1576 | |||
1577 | mMousePositionModified = TRUE; | ||
1578 | if (!mWindowHandle) | ||
1579 | { | ||
1580 | return FALSE; | ||
1581 | } | ||
1582 | |||
1583 | if (!convertCoords(position, &screen_pos)) | ||
1584 | { | ||
1585 | return FALSE; | ||
1586 | } | ||
1587 | |||
1588 | return SetCursorPos(screen_pos.mX, screen_pos.mY); | ||
1589 | } | ||
1590 | |||
1591 | BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position) | ||
1592 | { | ||
1593 | POINT cursor_point; | ||
1594 | LLCoordScreen screen_pos; | ||
1595 | |||
1596 | if (!mWindowHandle || | ||
1597 | !GetCursorPos(&cursor_point)) | ||
1598 | { | ||
1599 | return FALSE; | ||
1600 | } | ||
1601 | |||
1602 | screen_pos.mX = cursor_point.x; | ||
1603 | screen_pos.mY = cursor_point.y; | ||
1604 | |||
1605 | return convertCoords(screen_pos, position); | ||
1606 | } | ||
1607 | |||
1608 | void LLWindowWin32::hideCursor() | ||
1609 | { | ||
1610 | while (ShowCursor(FALSE) >= 0) | ||
1611 | { | ||
1612 | // nothing, wait for cursor to push down | ||
1613 | } | ||
1614 | mCursorHidden = TRUE; | ||
1615 | mHideCursorPermanent = TRUE; | ||
1616 | } | ||
1617 | |||
1618 | void LLWindowWin32::showCursor() | ||
1619 | { | ||
1620 | // makes sure the cursor shows up | ||
1621 | while (ShowCursor(TRUE) < 0) | ||
1622 | { | ||
1623 | // do nothing, wait for cursor to pop out | ||
1624 | } | ||
1625 | mCursorHidden = FALSE; | ||
1626 | mHideCursorPermanent = FALSE; | ||
1627 | } | ||
1628 | |||
1629 | void LLWindowWin32::showCursorFromMouseMove() | ||
1630 | { | ||
1631 | if (!mHideCursorPermanent) | ||
1632 | { | ||
1633 | showCursor(); | ||
1634 | } | ||
1635 | } | ||
1636 | |||
1637 | void LLWindowWin32::hideCursorUntilMouseMove() | ||
1638 | { | ||
1639 | if (!mHideCursorPermanent) | ||
1640 | { | ||
1641 | hideCursor(); | ||
1642 | mHideCursorPermanent = FALSE; | ||
1643 | } | ||
1644 | } | ||
1645 | |||
1646 | BOOL LLWindowWin32::isCursorHidden() | ||
1647 | { | ||
1648 | return mCursorHidden; | ||
1649 | } | ||
1650 | |||
1651 | |||
1652 | HCURSOR LLWindowWin32::loadColorCursor(LPCTSTR name) | ||
1653 | { | ||
1654 | return (HCURSOR)LoadImage(mhInstance, | ||
1655 | name, | ||
1656 | IMAGE_CURSOR, | ||
1657 | 0, // default width | ||
1658 | 0, // default height | ||
1659 | LR_DEFAULTCOLOR); | ||
1660 | } | ||
1661 | |||
1662 | |||
1663 | void LLWindowWin32::initCursors() | ||
1664 | { | ||
1665 | mCursor[ UI_CURSOR_ARROW ] = LoadCursor(NULL, IDC_ARROW); | ||
1666 | mCursor[ UI_CURSOR_WAIT ] = LoadCursor(NULL, IDC_WAIT); | ||
1667 | mCursor[ UI_CURSOR_HAND ] = LoadCursor(NULL, IDC_HAND); | ||
1668 | mCursor[ UI_CURSOR_IBEAM ] = LoadCursor(NULL, IDC_IBEAM); | ||
1669 | mCursor[ UI_CURSOR_CROSS ] = LoadCursor(NULL, IDC_CROSS); | ||
1670 | mCursor[ UI_CURSOR_SIZENWSE ] = LoadCursor(NULL, IDC_SIZENWSE); | ||
1671 | mCursor[ UI_CURSOR_SIZENESW ] = LoadCursor(NULL, IDC_SIZENESW); | ||
1672 | mCursor[ UI_CURSOR_SIZEWE ] = LoadCursor(NULL, IDC_SIZEWE); | ||
1673 | mCursor[ UI_CURSOR_SIZENS ] = LoadCursor(NULL, IDC_SIZENS); | ||
1674 | mCursor[ UI_CURSOR_NO ] = LoadCursor(NULL, IDC_NO); | ||
1675 | mCursor[ UI_CURSOR_WORKING ] = LoadCursor(NULL, IDC_APPSTARTING); | ||
1676 | |||
1677 | HMODULE module = GetModuleHandle(NULL); | ||
1678 | mCursor[ UI_CURSOR_TOOLGRAB ] = LoadCursor(module, TEXT("TOOLGRAB")); | ||
1679 | mCursor[ UI_CURSOR_TOOLLAND ] = LoadCursor(module, TEXT("TOOLLAND")); | ||
1680 | mCursor[ UI_CURSOR_TOOLFOCUS ] = LoadCursor(module, TEXT("TOOLFOCUS")); | ||
1681 | mCursor[ UI_CURSOR_TOOLCREATE ] = LoadCursor(module, TEXT("TOOLCREATE")); | ||
1682 | mCursor[ UI_CURSOR_ARROWDRAG ] = LoadCursor(module, TEXT("ARROWDRAG")); | ||
1683 | mCursor[ UI_CURSOR_ARROWCOPY ] = LoadCursor(module, TEXT("ARROWCOPY")); | ||
1684 | mCursor[ UI_CURSOR_ARROWDRAGMULTI ] = LoadCursor(module, TEXT("ARROWDRAGMULTI")); | ||
1685 | mCursor[ UI_CURSOR_ARROWCOPYMULTI ] = LoadCursor(module, TEXT("ARROWCOPYMULTI")); | ||
1686 | mCursor[ UI_CURSOR_NOLOCKED ] = LoadCursor(module, TEXT("NOLOCKED")); | ||
1687 | mCursor[ UI_CURSOR_ARROWLOCKED ]= LoadCursor(module, TEXT("ARROWLOCKED")); | ||
1688 | mCursor[ UI_CURSOR_GRABLOCKED ] = LoadCursor(module, TEXT("GRABLOCKED")); | ||
1689 | mCursor[ UI_CURSOR_TOOLTRANSLATE ] = LoadCursor(module, TEXT("TOOLTRANSLATE")); | ||
1690 | mCursor[ UI_CURSOR_TOOLROTATE ] = LoadCursor(module, TEXT("TOOLROTATE")); | ||
1691 | mCursor[ UI_CURSOR_TOOLSCALE ] = LoadCursor(module, TEXT("TOOLSCALE")); | ||
1692 | mCursor[ UI_CURSOR_TOOLCAMERA ] = LoadCursor(module, TEXT("TOOLCAMERA")); | ||
1693 | mCursor[ UI_CURSOR_TOOLPAN ] = LoadCursor(module, TEXT("TOOLPAN")); | ||
1694 | mCursor[ UI_CURSOR_TOOLZOOMIN ] = LoadCursor(module, TEXT("TOOLZOOMIN")); | ||
1695 | mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3")); | ||
1696 | mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE")); | ||
1697 | |||
1698 | // Color cursors | ||
1699 | mCursor[UI_CURSOR_TOOLSIT] = loadColorCursor(TEXT("TOOLSIT")); | ||
1700 | mCursor[UI_CURSOR_TOOLBUY] = loadColorCursor(TEXT("TOOLBUY")); | ||
1701 | mCursor[UI_CURSOR_TOOLPAY] = loadColorCursor(TEXT("TOOLPAY")); | ||
1702 | mCursor[UI_CURSOR_TOOLOPEN] = loadColorCursor(TEXT("TOOLOPEN")); | ||
1703 | |||
1704 | // Note: custom cursors that are not found make LoadCursor() return NULL. | ||
1705 | for( S32 i = 0; i < UI_CURSOR_COUNT; i++ ) | ||
1706 | { | ||
1707 | if( !mCursor[i] ) | ||
1708 | { | ||
1709 | mCursor[i] = LoadCursor(NULL, IDC_ARROW); | ||
1710 | } | ||
1711 | } | ||
1712 | } | ||
1713 | |||
1714 | |||
1715 | |||
1716 | void LLWindowWin32::setCursor(ECursorType cursor) | ||
1717 | { | ||
1718 | if (cursor == UI_CURSOR_ARROW | ||
1719 | && mBusyCount > 0) | ||
1720 | { | ||
1721 | cursor = UI_CURSOR_WORKING; | ||
1722 | } | ||
1723 | |||
1724 | if( mCurrentCursor != cursor ) | ||
1725 | { | ||
1726 | mCurrentCursor = cursor; | ||
1727 | SetCursor( mCursor[cursor] ); | ||
1728 | } | ||
1729 | } | ||
1730 | |||
1731 | ECursorType LLWindowWin32::getCursor() | ||
1732 | { | ||
1733 | return mCurrentCursor; | ||
1734 | } | ||
1735 | |||
1736 | void LLWindowWin32::captureMouse() | ||
1737 | { | ||
1738 | SetCapture(mWindowHandle); | ||
1739 | } | ||
1740 | |||
1741 | void LLWindowWin32::releaseMouse() | ||
1742 | { | ||
1743 | ReleaseCapture(); | ||
1744 | } | ||
1745 | |||
1746 | |||
1747 | void LLWindowWin32::delayInputProcessing() | ||
1748 | { | ||
1749 | mInputProcessingPaused = TRUE; | ||
1750 | } | ||
1751 | |||
1752 | void LLWindowWin32::gatherInput() | ||
1753 | { | ||
1754 | MSG msg; | ||
1755 | int msg_count = 0; | ||
1756 | |||
1757 | while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg_count < MAX_MESSAGE_PER_UPDATE) | ||
1758 | { | ||
1759 | TranslateMessage(&msg); | ||
1760 | DispatchMessage(&msg); | ||
1761 | msg_count++; | ||
1762 | |||
1763 | if ( mInputProcessingPaused ) | ||
1764 | { | ||
1765 | break; | ||
1766 | } | ||
1767 | /* Attempted workaround for problem where typing fast and hitting | ||
1768 | return would result in only part of the text being sent. JC | ||
1769 | |||
1770 | BOOL key_posted = TranslateMessage(&msg); | ||
1771 | DispatchMessage(&msg); | ||
1772 | msg_count++; | ||
1773 | |||
1774 | // If a key was translated, a WM_CHAR might have been posted to the end | ||
1775 | // of the event queue. We need it immediately. | ||
1776 | if (key_posted && msg.message == WM_KEYDOWN) | ||
1777 | { | ||
1778 | if (PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, PM_REMOVE)) | ||
1779 | { | ||
1780 | TranslateMessage(&msg); | ||
1781 | DispatchMessage(&msg); | ||
1782 | msg_count++; | ||
1783 | } | ||
1784 | } | ||
1785 | */ | ||
1786 | |||
1787 | // For async host by name support. Really hacky. | ||
1788 | if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message)) | ||
1789 | { | ||
1790 | gAsyncMsgCallback(msg); | ||
1791 | } | ||
1792 | } | ||
1793 | |||
1794 | mInputProcessingPaused = FALSE; | ||
1795 | |||
1796 | // clear this once we've processed all mouse messages that might have occurred after | ||
1797 | // we slammed the mouse position | ||
1798 | mMousePositionModified = FALSE; | ||
1799 | } | ||
1800 | |||
1801 | LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param) | ||
1802 | { | ||
1803 | LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA); | ||
1804 | |||
1805 | if (NULL != window_imp) | ||
1806 | { | ||
1807 | // Has user provided their own window callback? | ||
1808 | if (NULL != window_imp->mWndProc) | ||
1809 | { | ||
1810 | if (!window_imp->mWndProc(h_wnd, u_msg, w_param, l_param)) | ||
1811 | { | ||
1812 | // user has handled window message | ||
1813 | return 0; | ||
1814 | } | ||
1815 | } | ||
1816 | |||
1817 | // Juggle to make sure we can get negative positions for when | ||
1818 | // mouse is outside window. | ||
1819 | LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param)); | ||
1820 | |||
1821 | // This doesn't work, as LOWORD returns unsigned short. | ||
1822 | //LLCoordWindow window_coord(LOWORD(l_param), HIWORD(l_param)); | ||
1823 | LLCoordGL gl_coord; | ||
1824 | |||
1825 | // pass along extended flag in mask | ||
1826 | MASK mask = (l_param>>16 & KF_EXTENDED) ? MASK_EXTENDED : 0x0; | ||
1827 | BOOL eat_keystroke = TRUE; | ||
1828 | |||
1829 | switch(u_msg) | ||
1830 | { | ||
1831 | RECT update_rect; | ||
1832 | S32 update_width; | ||
1833 | S32 update_height; | ||
1834 | |||
1835 | case WM_TIMER: | ||
1836 | window_imp->updateJoystick( ); | ||
1837 | break; | ||
1838 | |||
1839 | case WM_PAINT: | ||
1840 | GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE); | ||
1841 | update_width = update_rect.right - update_rect.left + 1; | ||
1842 | update_height = update_rect.bottom - update_rect.top + 1; | ||
1843 | window_imp->mCallbacks->handlePaint(window_imp, update_rect.left, update_rect.top, | ||
1844 | update_width, update_height); | ||
1845 | break; | ||
1846 | case WM_PARENTNOTIFY: | ||
1847 | u_msg = u_msg; | ||
1848 | break; | ||
1849 | |||
1850 | case WM_SETCURSOR: | ||
1851 | // This message is sent whenever the cursor is moved in a window. | ||
1852 | // You need to set the appropriate cursor appearance. | ||
1853 | |||
1854 | // Only take control of cursor over client region of window | ||
1855 | // This allows Windows(tm) to handle resize cursors, etc. | ||
1856 | if (LOWORD(l_param) == HTCLIENT) | ||
1857 | { | ||
1858 | SetCursor(window_imp->mCursor[ window_imp->mCurrentCursor] ); | ||
1859 | return 0; | ||
1860 | } | ||
1861 | break; | ||
1862 | |||
1863 | case WM_ENTERMENULOOP: | ||
1864 | window_imp->mCallbacks->handleWindowBlock(window_imp); | ||
1865 | break; | ||
1866 | |||
1867 | case WM_EXITMENULOOP: | ||
1868 | window_imp->mCallbacks->handleWindowUnblock(window_imp); | ||
1869 | break; | ||
1870 | |||
1871 | case WM_ACTIVATEAPP: | ||
1872 | { | ||
1873 | // This message should be sent whenever the app gains or loses focus. | ||
1874 | BOOL activating = (BOOL) w_param; | ||
1875 | BOOL minimized = window_imp->getMinimized(); | ||
1876 | |||
1877 | if (gDebugWindowProc) | ||
1878 | { | ||
1879 | llinfos << "WINDOWPROC ActivateApp " | ||
1880 | << " activating " << S32(activating) | ||
1881 | << " minimized " << S32(minimized) | ||
1882 | << " fullscreen " << S32(window_imp->mFullscreen) | ||
1883 | << llendl; | ||
1884 | } | ||
1885 | |||
1886 | if (window_imp->mFullscreen) | ||
1887 | { | ||
1888 | // When we run fullscreen, restoring or minimizing the app needs | ||
1889 | // to switch the screen resolution | ||
1890 | if (activating) | ||
1891 | { | ||
1892 | window_imp->setFullscreenResolution(); | ||
1893 | window_imp->restore(); | ||
1894 | } | ||
1895 | else | ||
1896 | { | ||
1897 | window_imp->minimize(); | ||
1898 | window_imp->resetDisplayResolution(); | ||
1899 | } | ||
1900 | } | ||
1901 | break; | ||
1902 | } | ||
1903 | |||
1904 | case WM_ACTIVATE: | ||
1905 | { | ||
1906 | // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE | ||
1907 | BOOL activating = (LOWORD(w_param) != WA_INACTIVE); | ||
1908 | |||
1909 | BOOL minimized = BOOL(HIWORD(w_param)); | ||
1910 | |||
1911 | // JC - I'm not sure why, but if we don't report that we handled the | ||
1912 | // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work | ||
1913 | // properly when we run fullscreen. | ||
1914 | if (gDebugWindowProc) | ||
1915 | { | ||
1916 | llinfos << "WINDOWPROC Activate " | ||
1917 | << " activating " << S32(activating) | ||
1918 | << " minimized " << S32(minimized) | ||
1919 | << llendl; | ||
1920 | } | ||
1921 | |||
1922 | // Don't handle this. | ||
1923 | break; | ||
1924 | } | ||
1925 | |||
1926 | case WM_QUERYOPEN: | ||
1927 | // TODO: use this to return a nice icon | ||
1928 | break; | ||
1929 | |||
1930 | case WM_SYSCOMMAND: | ||
1931 | switch(w_param) | ||
1932 | { | ||
1933 | case SC_KEYMENU: | ||
1934 | // Disallow the ALT key from triggering the default system menu. | ||
1935 | return 0; | ||
1936 | |||
1937 | case SC_SCREENSAVE: | ||
1938 | case SC_MONITORPOWER: | ||
1939 | // eat screen save messages and prevent them! | ||
1940 | return 0; | ||
1941 | } | ||
1942 | break; | ||
1943 | |||
1944 | case WM_CLOSE: | ||
1945 | // Will the app allow the window to close? | ||
1946 | if (window_imp->mCallbacks->handleCloseRequest(window_imp)) | ||
1947 | { | ||
1948 | // Get the app to initiate cleanup. | ||
1949 | window_imp->mCallbacks->handleQuit(window_imp); | ||
1950 | // The app is responsible for calling destroyWindow when done with GL | ||
1951 | } | ||
1952 | return 0; | ||
1953 | |||
1954 | case WM_DESTROY: | ||
1955 | if (window_imp->shouldPostQuit()) | ||
1956 | { | ||
1957 | PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0 | ||
1958 | } | ||
1959 | return 0; | ||
1960 | |||
1961 | case WM_COMMAND: | ||
1962 | if (!HIWORD(w_param)) // this message is from a menu | ||
1963 | { | ||
1964 | window_imp->mCallbacks->handleMenuSelect(window_imp, LOWORD(w_param)); | ||
1965 | } | ||
1966 | break; | ||
1967 | |||
1968 | case WM_SYSKEYDOWN: | ||
1969 | // allow system keys, such as ALT-F4 to be processed by Windows | ||
1970 | eat_keystroke = FALSE; | ||
1971 | case WM_KEYDOWN: | ||
1972 | if (gDebugWindowProc) | ||
1973 | { | ||
1974 | llinfos << "Debug WindowProc WM_KEYDOWN " | ||
1975 | << " key " << S32(w_param) | ||
1976 | << llendl; | ||
1977 | } | ||
1978 | if (gKeyboard->handleKeyDown(w_param, mask) && eat_keystroke) | ||
1979 | { | ||
1980 | return 0; | ||
1981 | } | ||
1982 | // pass on to windows if we didn't handle it | ||
1983 | break; | ||
1984 | |||
1985 | case WM_SYSKEYUP: | ||
1986 | eat_keystroke = FALSE; | ||
1987 | case WM_KEYUP: | ||
1988 | if (gDebugWindowProc) | ||
1989 | { | ||
1990 | llinfos << "Debug WindowProc WM_KEYUP " | ||
1991 | << " key " << S32(w_param) | ||
1992 | << llendl; | ||
1993 | } | ||
1994 | if (gKeyboard->handleKeyUp(w_param, mask) && eat_keystroke) | ||
1995 | { | ||
1996 | return 0; | ||
1997 | } | ||
1998 | |||
1999 | // pass on to windows | ||
2000 | break; | ||
2001 | |||
2002 | |||
2003 | case WM_CHAR: | ||
2004 | // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need | ||
2005 | // to figure out how that works. - Doug | ||
2006 | // llinfos << "WM_CHAR: " << w_param << llendl; | ||
2007 | if (gDebugWindowProc) | ||
2008 | { | ||
2009 | llinfos << "Debug WindowProc WM_CHAR " | ||
2010 | << " key " << S32(w_param) | ||
2011 | << llendl; | ||
2012 | } | ||
2013 | if (window_imp->mCallbacks->handleUnicodeChar(w_param, gKeyboard->currentMask(FALSE))) | ||
2014 | { | ||
2015 | return 0; | ||
2016 | } | ||
2017 | break; | ||
2018 | |||
2019 | case WM_LBUTTONDOWN: | ||
2020 | { | ||
2021 | // Because we move the cursor position in the app, we need to query | ||
2022 | // to find out where the cursor at the time the event is handled. | ||
2023 | // If we don't do this, many clicks could get buffered up, and if the | ||
2024 | // first click changes the cursor position, all subsequent clicks | ||
2025 | // will occur at the wrong location. JC | ||
2026 | LLCoordWindow cursor_coord_window; | ||
2027 | if (window_imp->mMousePositionModified) | ||
2028 | { | ||
2029 | window_imp->getCursorPosition(&cursor_coord_window); | ||
2030 | window_imp->convertCoords(cursor_coord_window, &gl_coord); | ||
2031 | } | ||
2032 | else | ||
2033 | { | ||
2034 | window_imp->convertCoords(window_coord, &gl_coord); | ||
2035 | } | ||
2036 | MASK mask = gKeyboard->currentMask(TRUE); | ||
2037 | if (window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask)) | ||
2038 | { | ||
2039 | return 0; | ||
2040 | } | ||
2041 | } | ||
2042 | break; | ||
2043 | |||
2044 | case WM_LBUTTONDBLCLK: | ||
2045 | //RN: ignore right button double clicks for now | ||
2046 | //case WM_RBUTTONDBLCLK: | ||
2047 | { | ||
2048 | // Because we move the cursor position in the app, we need to query | ||
2049 | // to find out where the cursor at the time the event is handled. | ||
2050 | // If we don't do this, many clicks could get buffered up, and if the | ||
2051 | // first click changes the cursor position, all subsequent clicks | ||
2052 | // will occur at the wrong location. JC | ||
2053 | LLCoordWindow cursor_coord_window; | ||
2054 | if (window_imp->mMousePositionModified) | ||
2055 | { | ||
2056 | window_imp->getCursorPosition(&cursor_coord_window); | ||
2057 | window_imp->convertCoords(cursor_coord_window, &gl_coord); | ||
2058 | } | ||
2059 | else | ||
2060 | { | ||
2061 | window_imp->convertCoords(window_coord, &gl_coord); | ||
2062 | } | ||
2063 | MASK mask = gKeyboard->currentMask(TRUE); | ||
2064 | if (window_imp->mCallbacks->handleDoubleClick(window_imp, gl_coord, mask) ) | ||
2065 | { | ||
2066 | return 0; | ||
2067 | } | ||
2068 | } | ||
2069 | break; | ||
2070 | |||
2071 | case WM_LBUTTONUP: | ||
2072 | { | ||
2073 | //if (gDebugClicks) | ||
2074 | //{ | ||
2075 | // llinfos << "WndProc left button up" << llendl; | ||
2076 | //} | ||
2077 | // Because we move the cursor position in the app, we need to query | ||
2078 | // to find out where the cursor at the time the event is handled. | ||
2079 | // If we don't do this, many clicks could get buffered up, and if the | ||
2080 | // first click changes the cursor position, all subsequent clicks | ||
2081 | // will occur at the wrong location. JC | ||
2082 | LLCoordWindow cursor_coord_window; | ||
2083 | if (window_imp->mMousePositionModified) | ||
2084 | { | ||
2085 | window_imp->getCursorPosition(&cursor_coord_window); | ||
2086 | window_imp->convertCoords(cursor_coord_window, &gl_coord); | ||
2087 | } | ||
2088 | else | ||
2089 | { | ||
2090 | window_imp->convertCoords(window_coord, &gl_coord); | ||
2091 | } | ||
2092 | MASK mask = gKeyboard->currentMask(TRUE); | ||
2093 | if (window_imp->mCallbacks->handleMouseUp(window_imp, gl_coord, mask)) | ||
2094 | { | ||
2095 | return 0; | ||
2096 | } | ||
2097 | } | ||
2098 | break; | ||
2099 | |||
2100 | case WM_RBUTTONDBLCLK: | ||
2101 | case WM_RBUTTONDOWN: | ||
2102 | { | ||
2103 | // Because we move the cursor position in tllviewerhe app, we need to query | ||
2104 | // to find out where the cursor at the time the event is handled. | ||
2105 | // If we don't do this, many clicks could get buffered up, and if the | ||
2106 | // first click changes the cursor position, all subsequent clicks | ||
2107 | // will occur at the wrong location. JC | ||
2108 | LLCoordWindow cursor_coord_window; | ||
2109 | if (window_imp->mMousePositionModified) | ||
2110 | { | ||
2111 | window_imp->getCursorPosition(&cursor_coord_window); | ||
2112 | window_imp->convertCoords(cursor_coord_window, &gl_coord); | ||
2113 | } | ||
2114 | else | ||
2115 | { | ||
2116 | window_imp->convertCoords(window_coord, &gl_coord); | ||
2117 | } | ||
2118 | MASK mask = gKeyboard->currentMask(TRUE); | ||
2119 | if (window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask)) | ||
2120 | { | ||
2121 | return 0; | ||
2122 | } | ||
2123 | } | ||
2124 | break; | ||
2125 | |||
2126 | case WM_RBUTTONUP: | ||
2127 | { | ||
2128 | // Because we move the cursor position in the app, we need to query | ||
2129 | // to find out where the cursor at the time the event is handled. | ||
2130 | // If we don't do this, many clicks could get buffered up, and if the | ||
2131 | // first click changes the cursor position, all subsequent clicks | ||
2132 | // will occur at the wrong location. JC | ||
2133 | LLCoordWindow cursor_coord_window; | ||
2134 | if (window_imp->mMousePositionModified) | ||
2135 | { | ||
2136 | window_imp->getCursorPosition(&cursor_coord_window); | ||
2137 | window_imp->convertCoords(cursor_coord_window, &gl_coord); | ||
2138 | } | ||
2139 | else | ||
2140 | { | ||
2141 | window_imp->convertCoords(window_coord, &gl_coord); | ||
2142 | } | ||
2143 | MASK mask = gKeyboard->currentMask(TRUE); | ||
2144 | if (window_imp->mCallbacks->handleRightMouseUp(window_imp, gl_coord, mask)) | ||
2145 | { | ||
2146 | return 0; | ||
2147 | } | ||
2148 | } | ||
2149 | break; | ||
2150 | |||
2151 | case WM_MBUTTONDOWN: | ||
2152 | // Handle middle button click | ||
2153 | break; | ||
2154 | |||
2155 | case WM_MOUSEWHEEL: | ||
2156 | { | ||
2157 | static short z_delta = 0; | ||
2158 | |||
2159 | z_delta += HIWORD(w_param); | ||
2160 | // cout << "z_delta " << z_delta << endl; | ||
2161 | |||
2162 | // current mouse wheels report changes in increments of zDelta (+120, -120) | ||
2163 | // Future, higher resolution mouse wheels may report smaller deltas. | ||
2164 | // So we sum the deltas and only act when we've exceeded WHEEL_DELTA | ||
2165 | // | ||
2166 | // If the user rapidly spins the wheel, we can get messages with | ||
2167 | // large deltas, like 480 or so. Thus we need to scroll more quickly. | ||
2168 | if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta) | ||
2169 | { | ||
2170 | window_imp->mCallbacks->handleScrollWheel(window_imp, -z_delta / WHEEL_DELTA); | ||
2171 | z_delta = 0; | ||
2172 | } | ||
2173 | return 0; | ||
2174 | } | ||
2175 | /* | ||
2176 | // TODO: add this after resolving _WIN32_WINNT issue | ||
2177 | case WM_MOUSELEAVE: | ||
2178 | { | ||
2179 | window_imp->mCallbacks->handleMouseLeave(window_imp); | ||
2180 | |||
2181 | // TRACKMOUSEEVENT track_mouse_event; | ||
2182 | // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT ); | ||
2183 | // track_mouse_event.dwFlags = TME_LEAVE; | ||
2184 | // track_mouse_event.hwndTrack = h_wnd; | ||
2185 | // track_mouse_event.dwHoverTime = HOVER_DEFAULT; | ||
2186 | // TrackMouseEvent( &track_mouse_event ); | ||
2187 | return 0; | ||
2188 | } | ||
2189 | */ | ||
2190 | // Handle mouse movement within the window | ||
2191 | case WM_MOUSEMOVE: | ||
2192 | { | ||
2193 | window_imp->convertCoords(window_coord, &gl_coord); | ||
2194 | MASK mask = gKeyboard->currentMask(TRUE); | ||
2195 | window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask); | ||
2196 | return 0; | ||
2197 | } | ||
2198 | |||
2199 | case WM_SIZE: | ||
2200 | { | ||
2201 | S32 width = S32( LOWORD(l_param) ); | ||
2202 | S32 height = S32( HIWORD(l_param) ); | ||
2203 | |||
2204 | if (gDebugWindowProc) | ||
2205 | { | ||
2206 | BOOL maximized = ( w_param == SIZE_MAXIMIZED ); | ||
2207 | BOOL restored = ( w_param == SIZE_RESTORED ); | ||
2208 | BOOL minimized = ( w_param == SIZE_MINIMIZED ); | ||
2209 | |||
2210 | llinfos << "WINDOWPROC Size " | ||
2211 | << width << "x" << height | ||
2212 | << " max " << S32(maximized) | ||
2213 | << " min " << S32(minimized) | ||
2214 | << " rest " << S32(restored) | ||
2215 | << llendl; | ||
2216 | } | ||
2217 | |||
2218 | // If we are now restored, but we weren't before, this | ||
2219 | // means that the window was un-minimized. | ||
2220 | if (w_param == SIZE_RESTORED && window_imp->mLastSizeWParam != SIZE_RESTORED) | ||
2221 | { | ||
2222 | window_imp->mCallbacks->handleActivate(window_imp, TRUE); | ||
2223 | } | ||
2224 | |||
2225 | // handle case of window being maximized from fully minimized state | ||
2226 | if (w_param == SIZE_MAXIMIZED && window_imp->mLastSizeWParam != SIZE_MAXIMIZED) | ||
2227 | { | ||
2228 | window_imp->mCallbacks->handleActivate(window_imp, TRUE); | ||
2229 | } | ||
2230 | |||
2231 | // Also handle the minimization case | ||
2232 | if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED) | ||
2233 | { | ||
2234 | window_imp->mCallbacks->handleActivate(window_imp, FALSE); | ||
2235 | } | ||
2236 | |||
2237 | // Actually resize all of our views | ||
2238 | if (w_param != SIZE_MINIMIZED) | ||
2239 | { | ||
2240 | // Ignore updates for minimizing and minimized "windows" | ||
2241 | window_imp->mCallbacks->handleResize( window_imp, | ||
2242 | LOWORD(l_param), | ||
2243 | HIWORD(l_param) ); | ||
2244 | } | ||
2245 | |||
2246 | window_imp->mLastSizeWParam = w_param; | ||
2247 | |||
2248 | return 0; | ||
2249 | } | ||
2250 | |||
2251 | case WM_SETFOCUS: | ||
2252 | if (gDebugWindowProc) | ||
2253 | { | ||
2254 | llinfos << "WINDOWPROC SetFocus" << llendl; | ||
2255 | } | ||
2256 | window_imp->mCallbacks->handleFocus(window_imp); | ||
2257 | return 0; | ||
2258 | |||
2259 | case WM_KILLFOCUS: | ||
2260 | if (gDebugWindowProc) | ||
2261 | { | ||
2262 | llinfos << "WINDOWPROC KillFocus" << llendl; | ||
2263 | } | ||
2264 | window_imp->mCallbacks->handleFocusLost(window_imp); | ||
2265 | return 0; | ||
2266 | |||
2267 | case WM_COPYDATA: | ||
2268 | // received a URL | ||
2269 | PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT) l_param; | ||
2270 | window_imp->mCallbacks->handleDataCopy(window_imp, myCDS->dwData, myCDS->lpData); | ||
2271 | return 0; | ||
2272 | } | ||
2273 | } | ||
2274 | |||
2275 | // pass unhandled messages down to Windows | ||
2276 | return DefWindowProc(h_wnd, u_msg, w_param, l_param); | ||
2277 | } | ||
2278 | |||
2279 | BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to) | ||
2280 | { | ||
2281 | S32 client_height; | ||
2282 | RECT client_rect; | ||
2283 | LLCoordWindow window_position; | ||
2284 | |||
2285 | if (!mWindowHandle || | ||
2286 | !GetClientRect(mWindowHandle, &client_rect) || | ||
2287 | NULL == to) | ||
2288 | { | ||
2289 | return FALSE; | ||
2290 | } | ||
2291 | |||
2292 | to->mX = from.mX; | ||
2293 | client_height = client_rect.bottom - client_rect.top; | ||
2294 | to->mY = client_height - from.mY - 1; | ||
2295 | |||
2296 | return TRUE; | ||
2297 | } | ||
2298 | |||
2299 | BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to) | ||
2300 | { | ||
2301 | S32 client_height; | ||
2302 | RECT client_rect; | ||
2303 | |||
2304 | if (!mWindowHandle || | ||
2305 | !GetClientRect(mWindowHandle, &client_rect) || | ||
2306 | NULL == to) | ||
2307 | { | ||
2308 | return FALSE; | ||
2309 | } | ||
2310 | |||
2311 | to->mX = from.mX; | ||
2312 | client_height = client_rect.bottom - client_rect.top; | ||
2313 | to->mY = client_height - from.mY - 1; | ||
2314 | |||
2315 | return TRUE; | ||
2316 | } | ||
2317 | |||
2318 | BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to) | ||
2319 | { | ||
2320 | POINT mouse_point; | ||
2321 | |||
2322 | mouse_point.x = from.mX; | ||
2323 | mouse_point.y = from.mY; | ||
2324 | BOOL result = ScreenToClient(mWindowHandle, &mouse_point); | ||
2325 | |||
2326 | if (result) | ||
2327 | { | ||
2328 | to->mX = mouse_point.x; | ||
2329 | to->mY = mouse_point.y; | ||
2330 | } | ||
2331 | |||
2332 | return result; | ||
2333 | } | ||
2334 | |||
2335 | BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to) | ||
2336 | { | ||
2337 | POINT mouse_point; | ||
2338 | |||
2339 | mouse_point.x = from.mX; | ||
2340 | mouse_point.y = from.mY; | ||
2341 | BOOL result = ClientToScreen(mWindowHandle, &mouse_point); | ||
2342 | |||
2343 | if (result) | ||
2344 | { | ||
2345 | to->mX = mouse_point.x; | ||
2346 | to->mY = mouse_point.y; | ||
2347 | } | ||
2348 | |||
2349 | return result; | ||
2350 | } | ||
2351 | |||
2352 | BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to) | ||
2353 | { | ||
2354 | LLCoordWindow window_coord; | ||
2355 | |||
2356 | if (!mWindowHandle || (NULL == to)) | ||
2357 | { | ||
2358 | return FALSE; | ||
2359 | } | ||
2360 | |||
2361 | convertCoords(from, &window_coord); | ||
2362 | convertCoords(window_coord, to); | ||
2363 | return TRUE; | ||
2364 | } | ||
2365 | |||
2366 | BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to) | ||
2367 | { | ||
2368 | LLCoordWindow window_coord; | ||
2369 | |||
2370 | if (!mWindowHandle || (NULL == to)) | ||
2371 | { | ||
2372 | return FALSE; | ||
2373 | } | ||
2374 | |||
2375 | convertCoords(from, &window_coord); | ||
2376 | convertCoords(window_coord, to); | ||
2377 | return TRUE; | ||
2378 | } | ||
2379 | |||
2380 | |||
2381 | BOOL LLWindowWin32::isClipboardTextAvailable() | ||
2382 | { | ||
2383 | return IsClipboardFormatAvailable(CF_UNICODETEXT) || IsClipboardFormatAvailable( CF_TEXT ); | ||
2384 | } | ||
2385 | |||
2386 | |||
2387 | BOOL LLWindowWin32::pasteTextFromClipboard(LLWString &dst) | ||
2388 | { | ||
2389 | BOOL success = FALSE; | ||
2390 | |||
2391 | if (IsClipboardFormatAvailable(CF_UNICODETEXT)) | ||
2392 | { | ||
2393 | if (OpenClipboard(mWindowHandle)) | ||
2394 | { | ||
2395 | HGLOBAL h_data = GetClipboardData(CF_UNICODETEXT); | ||
2396 | if (h_data) | ||
2397 | { | ||
2398 | WCHAR *utf16str = (WCHAR*) GlobalLock(h_data); | ||
2399 | if (utf16str) | ||
2400 | { | ||
2401 | dst = utf16str_to_wstring(utf16str); | ||
2402 | LLWString::removeCRLF(dst); | ||
2403 | GlobalUnlock(h_data); | ||
2404 | success = TRUE; | ||
2405 | } | ||
2406 | } | ||
2407 | CloseClipboard(); | ||
2408 | } | ||
2409 | } | ||
2410 | else if (IsClipboardFormatAvailable(CF_TEXT)) | ||
2411 | { | ||
2412 | // This must be an OLD OS. We don't do non-ASCII for old OSes | ||
2413 | if (OpenClipboard(mWindowHandle)) | ||
2414 | { | ||
2415 | HGLOBAL h_data = GetClipboardData(CF_TEXT); | ||
2416 | if (h_data) | ||
2417 | { | ||
2418 | char* str = (char*) GlobalLock(h_data); | ||
2419 | if (str) | ||
2420 | { | ||
2421 | // Strip non-ASCII characters | ||
2422 | dst = utf8str_to_wstring(mbcsstring_makeASCII(str)); | ||
2423 | LLWString::removeCRLF(dst); | ||
2424 | GlobalUnlock(h_data); | ||
2425 | success = TRUE; | ||
2426 | } | ||
2427 | } | ||
2428 | CloseClipboard(); | ||
2429 | } | ||
2430 | } | ||
2431 | |||
2432 | return success; | ||
2433 | } | ||
2434 | |||
2435 | |||
2436 | BOOL LLWindowWin32::copyTextToClipboard(const LLWString& wstr) | ||
2437 | { | ||
2438 | BOOL success = FALSE; | ||
2439 | |||
2440 | if (OpenClipboard(mWindowHandle)) | ||
2441 | { | ||
2442 | EmptyClipboard(); | ||
2443 | |||
2444 | // Provide a copy of the data in Unicode format. | ||
2445 | LLWString sanitized_string(wstr); | ||
2446 | LLWString::addCRLF(sanitized_string); | ||
2447 | llutf16string out_utf16 = wstring_to_utf16str(sanitized_string); | ||
2448 | const size_t size_utf16 = (out_utf16.length() + 1) * sizeof(WCHAR); | ||
2449 | |||
2450 | // Memory is allocated and then ownership of it is transfered to the system. | ||
2451 | HGLOBAL hglobal_copy_utf16 = GlobalAlloc(GMEM_MOVEABLE, size_utf16); | ||
2452 | if (hglobal_copy_utf16) | ||
2453 | { | ||
2454 | WCHAR* copy_utf16 = (WCHAR*) GlobalLock(hglobal_copy_utf16); | ||
2455 | if (copy_utf16) | ||
2456 | { | ||
2457 | memcpy(copy_utf16, out_utf16.c_str(), size_utf16); | ||
2458 | GlobalUnlock(hglobal_copy_utf16); | ||
2459 | |||
2460 | if (SetClipboardData(CF_UNICODETEXT, hglobal_copy_utf16)) | ||
2461 | { | ||
2462 | success = TRUE; | ||
2463 | } | ||
2464 | } | ||
2465 | } | ||
2466 | |||
2467 | // Also provide a copy as raw ASCII text. | ||
2468 | LLWString ascii_string(wstr); | ||
2469 | LLWString::_makeASCII(ascii_string); | ||
2470 | LLWString::addCRLF(ascii_string); | ||
2471 | std::string out_s = wstring_to_utf8str(ascii_string); | ||
2472 | const size_t size = (out_s.length() + 1) * sizeof(char); | ||
2473 | |||
2474 | // Memory is allocated and then ownership of it is transfered to the system. | ||
2475 | HGLOBAL hglobal_copy = GlobalAlloc(GMEM_MOVEABLE, size); | ||
2476 | if (hglobal_copy) | ||
2477 | { | ||
2478 | char* copy = (char*) GlobalLock(hglobal_copy); | ||
2479 | if( copy ) | ||
2480 | { | ||
2481 | memcpy(copy, out_s.c_str(), size); | ||
2482 | GlobalUnlock(hglobal_copy); | ||
2483 | |||
2484 | if (SetClipboardData(CF_TEXT, hglobal_copy)) | ||
2485 | { | ||
2486 | success = TRUE; | ||
2487 | } | ||
2488 | } | ||
2489 | } | ||
2490 | |||
2491 | CloseClipboard(); | ||
2492 | } | ||
2493 | |||
2494 | return success; | ||
2495 | } | ||
2496 | |||
2497 | // Constrains the mouse to the window. | ||
2498 | void LLWindowWin32::setMouseClipping( BOOL b ) | ||
2499 | { | ||
2500 | if( b != mIsMouseClipping ) | ||
2501 | { | ||
2502 | BOOL success = FALSE; | ||
2503 | |||
2504 | if( b ) | ||
2505 | { | ||
2506 | GetClipCursor( &mOldMouseClip ); | ||
2507 | |||
2508 | RECT client_rect_in_screen_space; | ||
2509 | if( getClientRectInScreenSpace( &client_rect_in_screen_space ) ) | ||
2510 | { | ||
2511 | success = ClipCursor( &client_rect_in_screen_space ); | ||
2512 | } | ||
2513 | } | ||
2514 | else | ||
2515 | { | ||
2516 | // Must restore the old mouse clip, which may be set by another window. | ||
2517 | success = ClipCursor( &mOldMouseClip ); | ||
2518 | SetRect( &mOldMouseClip, 0, 0, 0, 0 ); | ||
2519 | } | ||
2520 | |||
2521 | if( success ) | ||
2522 | { | ||
2523 | mIsMouseClipping = b; | ||
2524 | } | ||
2525 | } | ||
2526 | } | ||
2527 | |||
2528 | BOOL LLWindowWin32::getClientRectInScreenSpace( RECT* rectp ) | ||
2529 | { | ||
2530 | BOOL success = FALSE; | ||
2531 | |||
2532 | RECT client_rect; | ||
2533 | if( mWindowHandle && GetClientRect(mWindowHandle, &client_rect) ) | ||
2534 | { | ||
2535 | POINT top_left; | ||
2536 | top_left.x = client_rect.left; | ||
2537 | top_left.y = client_rect.top; | ||
2538 | ClientToScreen(mWindowHandle, &top_left); | ||
2539 | |||
2540 | POINT bottom_right; | ||
2541 | bottom_right.x = client_rect.right; | ||
2542 | bottom_right.y = client_rect.bottom; | ||
2543 | ClientToScreen(mWindowHandle, &bottom_right); | ||
2544 | |||
2545 | SetRect( rectp, | ||
2546 | top_left.x, | ||
2547 | top_left.y, | ||
2548 | bottom_right.x, | ||
2549 | bottom_right.y ); | ||
2550 | |||
2551 | success = TRUE; | ||
2552 | } | ||
2553 | |||
2554 | return success; | ||
2555 | } | ||
2556 | |||
2557 | |||
2558 | BOOL LLWindowWin32::sendEmail(const char* address, const char* subject, const char* body_text, | ||
2559 | const char* attachment, const char* attachment_displayed_name ) | ||
2560 | { | ||
2561 | // Based on "A SendMail() DLL" by Greg Turner, Windows Developer Magazine, Nov. 1997. | ||
2562 | // See article for use of GetProcAddress | ||
2563 | // No restrictions on use. | ||
2564 | |||
2565 | enum SendResult | ||
2566 | { | ||
2567 | LL_EMAIL_SUCCESS, | ||
2568 | LL_EMAIL_MAPI_NOT_INSTALLED, // No MAPI Server (eg Microsoft Exchange) installed | ||
2569 | LL_EMAIL_MAPILOAD_FAILED, // Load of MAPI32.DLL failed | ||
2570 | LL_EMAIL_SEND_FAILED // The message send itself failed | ||
2571 | }; | ||
2572 | |||
2573 | SendResult result = LL_EMAIL_SUCCESS; | ||
2574 | |||
2575 | U32 mapi_installed = GetProfileInt(L"Mail", L"MAPI", 0); | ||
2576 | if( !mapi_installed) | ||
2577 | { | ||
2578 | result = LL_EMAIL_MAPI_NOT_INSTALLED; | ||
2579 | } | ||
2580 | else | ||
2581 | { | ||
2582 | HINSTANCE hMAPIInst = LoadLibrary(L"MAPI32.DLL"); | ||
2583 | if(!hMAPIInst) | ||
2584 | { | ||
2585 | result = LL_EMAIL_MAPILOAD_FAILED; | ||
2586 | } | ||
2587 | else | ||
2588 | { | ||
2589 | LPMAPISENDMAIL pMAPISendMail = (LPMAPISENDMAIL) GetProcAddress(hMAPIInst, "MAPISendMail"); | ||
2590 | |||
2591 | // Send the message | ||
2592 | MapiRecipDesc recipients[1]; | ||
2593 | recipients[0].ulReserved = 0; | ||
2594 | recipients[0].ulRecipClass = MAPI_TO; | ||
2595 | recipients[0].lpszName = (char*)address; | ||
2596 | recipients[0].lpszAddress = (char*)address; | ||
2597 | recipients[0].ulEIDSize = 0; | ||
2598 | recipients[0].lpEntryID = 0; | ||
2599 | |||
2600 | MapiFileDesc files[1]; | ||
2601 | files[0].ulReserved = 0; | ||
2602 | files[0].flFlags = 0; // non-OLE file | ||
2603 | files[0].nPosition = -1; // Leave file location in email unspecified. | ||
2604 | files[0].lpszPathName = (char*)attachment; // Must be fully qualified name, including drive letter. | ||
2605 | files[0].lpszFileName = (char*)attachment_displayed_name; // If NULL, uses attachment as displayed name. | ||
2606 | files[0].lpFileType = NULL; // Recipient will have to figure out what kind of file this is. | ||
2607 | |||
2608 | MapiMessage msg; | ||
2609 | memset(&msg, 0, sizeof(msg)); | ||
2610 | msg.lpszSubject = (char*)subject; // may be NULL | ||
2611 | msg.lpszNoteText = (char*)body_text; | ||
2612 | msg.nRecipCount = address ? 1 : 0; | ||
2613 | msg.lpRecips = address ? recipients : NULL; | ||
2614 | msg.nFileCount = attachment ? 1 : 0; | ||
2615 | msg.lpFiles = attachment ? files : NULL; | ||
2616 | |||
2617 | U32 success = pMAPISendMail(0, (U32) mWindowHandle, &msg, MAPI_DIALOG|MAPI_LOGON_UI|MAPI_NEW_SESSION, 0); | ||
2618 | if(success != SUCCESS_SUCCESS) | ||
2619 | { | ||
2620 | result = LL_EMAIL_SEND_FAILED; | ||
2621 | } | ||
2622 | |||
2623 | FreeLibrary(hMAPIInst); | ||
2624 | } | ||
2625 | } | ||
2626 | |||
2627 | return result == LL_EMAIL_SUCCESS; | ||
2628 | } | ||
2629 | |||
2630 | |||
2631 | S32 LLWindowWin32::stat(const char* file_name, struct stat* stat_info) | ||
2632 | { | ||
2633 | llassert( sizeof(struct stat) == sizeof(struct _stat) ); // They are defined identically in sys/stat.h, but I'm paranoid. | ||
2634 | return LLFile::stat( file_name, (struct _stat*) stat_info ); | ||
2635 | } | ||
2636 | |||
2637 | void LLWindowWin32::flashIcon(F32 seconds) | ||
2638 | { | ||
2639 | FLASHWINFO flash_info; | ||
2640 | |||
2641 | flash_info.cbSize = sizeof(FLASHWINFO); | ||
2642 | flash_info.hwnd = mWindowHandle; | ||
2643 | flash_info.dwFlags = FLASHW_TRAY; | ||
2644 | flash_info.uCount = UINT(seconds / ICON_FLASH_TIME); | ||
2645 | flash_info.dwTimeout = DWORD(1000.f * ICON_FLASH_TIME); // milliseconds | ||
2646 | FlashWindowEx(&flash_info); | ||
2647 | } | ||
2648 | |||
2649 | F32 LLWindowWin32::getGamma() | ||
2650 | { | ||
2651 | return mCurrentGamma; | ||
2652 | } | ||
2653 | |||
2654 | BOOL LLWindowWin32::restoreGamma() | ||
2655 | { | ||
2656 | return SetDeviceGammaRamp(mhDC, mPrevGammaRamp); | ||
2657 | } | ||
2658 | |||
2659 | BOOL LLWindowWin32::setGamma(const F32 gamma) | ||
2660 | { | ||
2661 | mCurrentGamma = gamma; | ||
2662 | |||
2663 | llinfos << "Setting gamma to " << gamma << llendl; | ||
2664 | |||
2665 | for ( int i = 0; i < 256; ++i ) | ||
2666 | { | ||
2667 | int mult = 256 - ( int ) ( ( gamma - 1.0f ) * 128.0f ); | ||
2668 | |||
2669 | int value = mult * i; | ||
2670 | |||
2671 | if ( value > 0xffff ) | ||
2672 | value = 0xffff; | ||
2673 | |||
2674 | mCurrentGammaRamp [ 0 * 256 + i ] = | ||
2675 | mCurrentGammaRamp [ 1 * 256 + i ] = | ||
2676 | mCurrentGammaRamp [ 2 * 256 + i ] = ( WORD )value; | ||
2677 | }; | ||
2678 | |||
2679 | return SetDeviceGammaRamp ( mhDC, mCurrentGammaRamp ); | ||
2680 | } | ||
2681 | |||
2682 | LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_resolutions) | ||
2683 | { | ||
2684 | if (!mSupportedResolutions) | ||
2685 | { | ||
2686 | mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS]; | ||
2687 | DEVMODE dev_mode; | ||
2688 | |||
2689 | mNumSupportedResolutions = 0; | ||
2690 | for (S32 mode_num = 0; mNumSupportedResolutions < MAX_NUM_RESOLUTIONS; mode_num++) | ||
2691 | { | ||
2692 | if (!EnumDisplaySettings(NULL, mode_num, &dev_mode)) | ||
2693 | { | ||
2694 | break; | ||
2695 | } | ||
2696 | |||
2697 | if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL && | ||
2698 | dev_mode.dmPelsWidth >= 800 && | ||
2699 | dev_mode.dmPelsHeight >= 600) | ||
2700 | { | ||
2701 | BOOL resolution_exists = FALSE; | ||
2702 | for(S32 i = 0; i < mNumSupportedResolutions; i++) | ||
2703 | { | ||
2704 | if (mSupportedResolutions[i].mWidth == dev_mode.dmPelsWidth && | ||
2705 | mSupportedResolutions[i].mHeight == dev_mode.dmPelsHeight) | ||
2706 | { | ||
2707 | resolution_exists = TRUE; | ||
2708 | } | ||
2709 | } | ||
2710 | if (!resolution_exists) | ||
2711 | { | ||
2712 | mSupportedResolutions[mNumSupportedResolutions].mWidth = dev_mode.dmPelsWidth; | ||
2713 | mSupportedResolutions[mNumSupportedResolutions].mHeight = dev_mode.dmPelsHeight; | ||
2714 | mNumSupportedResolutions++; | ||
2715 | } | ||
2716 | } | ||
2717 | } | ||
2718 | } | ||
2719 | |||
2720 | num_resolutions = mNumSupportedResolutions; | ||
2721 | return mSupportedResolutions; | ||
2722 | } | ||
2723 | |||
2724 | |||
2725 | F32 LLWindowWin32::getNativeAspectRatio() | ||
2726 | { | ||
2727 | if (mOverrideAspectRatio > 0.f) | ||
2728 | { | ||
2729 | return mOverrideAspectRatio; | ||
2730 | } | ||
2731 | else if (mNativeAspectRatio > 0.f) | ||
2732 | { | ||
2733 | // we grabbed this value at startup, based on the user's desktop settings | ||
2734 | return mNativeAspectRatio; | ||
2735 | } | ||
2736 | // RN: this hack presumes that the largest supported resolution is monitor-limited | ||
2737 | // and that pixels in that mode are square, therefore defining the native aspect ratio | ||
2738 | // of the monitor...this seems to work to a close approximation for most CRTs/LCDs | ||
2739 | S32 num_resolutions; | ||
2740 | LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions); | ||
2741 | |||
2742 | return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight); | ||
2743 | } | ||
2744 | |||
2745 | F32 LLWindowWin32::getPixelAspectRatio() | ||
2746 | { | ||
2747 | F32 pixel_aspect = 1.f; | ||
2748 | if (getFullscreen()) | ||
2749 | { | ||
2750 | LLCoordScreen screen_size; | ||
2751 | getSize(&screen_size); | ||
2752 | pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX; | ||
2753 | } | ||
2754 | |||
2755 | return pixel_aspect; | ||
2756 | } | ||
2757 | |||
2758 | // Change display resolution. Returns true if successful. | ||
2759 | // protected | ||
2760 | BOOL LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh) | ||
2761 | { | ||
2762 | DEVMODE dev_mode; | ||
2763 | dev_mode.dmSize = sizeof(dev_mode); | ||
2764 | BOOL success = FALSE; | ||
2765 | |||
2766 | // Don't change anything if we don't have to | ||
2767 | if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) | ||
2768 | { | ||
2769 | if (dev_mode.dmPelsWidth == width && | ||
2770 | dev_mode.dmPelsHeight == height && | ||
2771 | dev_mode.dmBitsPerPel == bits && | ||
2772 | dev_mode.dmDisplayFrequency == refresh ) | ||
2773 | { | ||
2774 | // ...display mode identical, do nothing | ||
2775 | return TRUE; | ||
2776 | } | ||
2777 | } | ||
2778 | |||
2779 | memset(&dev_mode, 0, sizeof(dev_mode)); | ||
2780 | dev_mode.dmSize = sizeof(dev_mode); | ||
2781 | dev_mode.dmPelsWidth = width; | ||
2782 | dev_mode.dmPelsHeight = height; | ||
2783 | dev_mode.dmBitsPerPel = bits; | ||
2784 | dev_mode.dmDisplayFrequency = refresh; | ||
2785 | dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; | ||
2786 | |||
2787 | // CDS_FULLSCREEN indicates that this is a temporary change to the device mode. | ||
2788 | LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN); | ||
2789 | |||
2790 | success = (DISP_CHANGE_SUCCESSFUL == cds_result); | ||
2791 | |||
2792 | if (!success) | ||
2793 | { | ||
2794 | llwarns << "setDisplayResolution failed, " | ||
2795 | << width << "x" << height << "x" << bits << " @ " << refresh << llendl; | ||
2796 | } | ||
2797 | |||
2798 | return success; | ||
2799 | } | ||
2800 | |||
2801 | // protected | ||
2802 | BOOL LLWindowWin32::setFullscreenResolution() | ||
2803 | { | ||
2804 | if (mFullscreen) | ||
2805 | { | ||
2806 | return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh); | ||
2807 | } | ||
2808 | else | ||
2809 | { | ||
2810 | return FALSE; | ||
2811 | } | ||
2812 | } | ||
2813 | |||
2814 | // protected | ||
2815 | BOOL LLWindowWin32::resetDisplayResolution() | ||
2816 | { | ||
2817 | llinfos << "resetDisplayResolution START" << llendl; | ||
2818 | |||
2819 | LONG cds_result = ChangeDisplaySettings(NULL, 0); | ||
2820 | |||
2821 | BOOL success = (DISP_CHANGE_SUCCESSFUL == cds_result); | ||
2822 | |||
2823 | if (!success) | ||
2824 | { | ||
2825 | llwarns << "resetDisplayResolution failed" << llendl; | ||
2826 | } | ||
2827 | |||
2828 | llinfos << "resetDisplayResolution END" << llendl; | ||
2829 | |||
2830 | return success; | ||
2831 | } | ||
2832 | |||
2833 | void LLWindowWin32::swapBuffers() | ||
2834 | { | ||
2835 | SwapBuffers(mhDC); | ||
2836 | } | ||
2837 | |||
2838 | |||
2839 | BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, | ||
2840 | VOID* pContext ) | ||
2841 | { | ||
2842 | HRESULT hr; | ||
2843 | |||
2844 | // Obtain an interface to the enumerated joystick. | ||
2845 | hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick, NULL ); | ||
2846 | |||
2847 | // If it failed, then we can't use this joystick. (Maybe the user unplugged | ||
2848 | // it while we were in the middle of enumerating it.) | ||
2849 | if( FAILED(hr) ) | ||
2850 | return DIENUM_CONTINUE; | ||
2851 | |||
2852 | // Stop enumeration. Note: we're just taking the first joystick we get. You | ||
2853 | // could store all the enumerated joysticks and let the user pick. | ||
2854 | return DIENUM_STOP; | ||
2855 | } | ||
2856 | |||
2857 | BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, | ||
2858 | VOID* pContext ) | ||
2859 | { | ||
2860 | if( pdidoi->dwType & DIDFT_AXIS ) | ||
2861 | { | ||
2862 | DIPROPRANGE diprg; | ||
2863 | diprg.diph.dwSize = sizeof(DIPROPRANGE); | ||
2864 | diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); | ||
2865 | diprg.diph.dwHow = DIPH_BYID; | ||
2866 | diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis | ||
2867 | diprg.lMin = -1000; | ||
2868 | diprg.lMax = +1000; | ||
2869 | |||
2870 | // Set the range for the axis | ||
2871 | if( FAILED( g_pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) ) ) | ||
2872 | return DIENUM_STOP; | ||
2873 | |||
2874 | } | ||
2875 | return DIENUM_CONTINUE; | ||
2876 | } | ||
2877 | |||
2878 | void LLWindowWin32::updateJoystick( ) | ||
2879 | { | ||
2880 | HRESULT hr; | ||
2881 | DIJOYSTATE js; // DInput joystick state | ||
2882 | |||
2883 | if (!g_pJoystick) | ||
2884 | return; | ||
2885 | hr = g_pJoystick->Poll(); | ||
2886 | if ( hr == DIERR_INPUTLOST ) | ||
2887 | { | ||
2888 | hr = g_pJoystick->Acquire(); | ||
2889 | return; | ||
2890 | } | ||
2891 | else if ( FAILED(hr) ) | ||
2892 | return; | ||
2893 | |||
2894 | // Get the input's device state | ||
2895 | if( FAILED( hr = g_pJoystick->GetDeviceState( sizeof(DIJOYSTATE), &js ) ) ) | ||
2896 | return; // The device should have been acquired during the Poll() | ||
2897 | |||
2898 | if (js.lX <= -500) | ||
2899 | { | ||
2900 | if (!(mJoyStickState & 0x1)) | ||
2901 | { | ||
2902 | gKeyboard->handleTranslatedKeyDown(KEY_PAD_LEFT, 0); | ||
2903 | mJoyStickState |= 0x1; | ||
2904 | } | ||
2905 | } | ||
2906 | else | ||
2907 | { | ||
2908 | if (mJoyStickState & 0x1) | ||
2909 | { | ||
2910 | gKeyboard->handleTranslatedKeyUp(KEY_PAD_LEFT, 0); | ||
2911 | mJoyStickState &= ~0x1; | ||
2912 | } | ||
2913 | } | ||
2914 | if (js.lX >= 500) | ||
2915 | { | ||
2916 | if (!(mJoyStickState & 0x2)) | ||
2917 | { | ||
2918 | gKeyboard->handleTranslatedKeyDown(KEY_PAD_RIGHT, 0); | ||
2919 | mJoyStickState |= 0x2; | ||
2920 | } | ||
2921 | } | ||
2922 | else | ||
2923 | { | ||
2924 | if (mJoyStickState & 0x2) | ||
2925 | { | ||
2926 | gKeyboard->handleTranslatedKeyUp(KEY_PAD_RIGHT, 0); | ||
2927 | mJoyStickState &= ~0x2; | ||
2928 | } | ||
2929 | } | ||
2930 | if (js.lY <= -500) | ||
2931 | { | ||
2932 | if (!(mJoyStickState & 0x4)) | ||
2933 | { | ||
2934 | gKeyboard->handleTranslatedKeyDown(KEY_PAD_UP, 0); | ||
2935 | mJoyStickState |= 0x4; | ||
2936 | } | ||
2937 | } | ||
2938 | else | ||
2939 | { | ||
2940 | if (mJoyStickState & 0x4) | ||
2941 | { | ||
2942 | gKeyboard->handleTranslatedKeyUp(KEY_PAD_UP, 0); | ||
2943 | mJoyStickState &= ~0x4; | ||
2944 | } | ||
2945 | } | ||
2946 | if (js.lY >= 500) | ||
2947 | { | ||
2948 | if (!(mJoyStickState & 0x8)) | ||
2949 | { | ||
2950 | gKeyboard->handleTranslatedKeyDown(KEY_PAD_DOWN, 0); | ||
2951 | mJoyStickState |= 0x8; | ||
2952 | } | ||
2953 | } | ||
2954 | else | ||
2955 | { | ||
2956 | if (mJoyStickState & 0x8) | ||
2957 | { | ||
2958 | gKeyboard->handleTranslatedKeyUp(KEY_PAD_DOWN, 0); | ||
2959 | mJoyStickState &= ~0x8; | ||
2960 | } | ||
2961 | } | ||
2962 | |||
2963 | for( int i = 0; i < 15; i++ ) | ||
2964 | { | ||
2965 | if ( js.rgbButtons[i] & 0x80 ) | ||
2966 | { | ||
2967 | if (!(mJoyButtonState & (1<<i))) | ||
2968 | { | ||
2969 | gKeyboard->handleTranslatedKeyDown(KEY_BUTTON1+i, 0); | ||
2970 | mJoyButtonState |= (1<<i); | ||
2971 | } | ||
2972 | } | ||
2973 | else | ||
2974 | { | ||
2975 | if (mJoyButtonState & (1<<i)) | ||
2976 | { | ||
2977 | gKeyboard->handleTranslatedKeyUp(KEY_BUTTON1+i, 0); | ||
2978 | mJoyButtonState &= ~(1<<i); | ||
2979 | } | ||
2980 | } | ||
2981 | } | ||
2982 | } | ||
2983 | |||
2984 | |||
2985 | // | ||
2986 | // LLSplashScreenImp | ||
2987 | // | ||
2988 | LLSplashScreenWin32::LLSplashScreenWin32() | ||
2989 | : mWindow(NULL) | ||
2990 | { | ||
2991 | } | ||
2992 | |||
2993 | LLSplashScreenWin32::~LLSplashScreenWin32() | ||
2994 | { | ||
2995 | } | ||
2996 | |||
2997 | void LLSplashScreenWin32::showImpl() | ||
2998 | { | ||
2999 | // This appears to work. ??? | ||
3000 | HINSTANCE hinst = GetModuleHandle(NULL); | ||
3001 | |||
3002 | mWindow = CreateDialog(hinst, | ||
3003 | TEXT("SPLASHSCREEN"), | ||
3004 | NULL, // no parent | ||
3005 | (DLGPROC) LLSplashScreenWin32::windowProc); | ||
3006 | ShowWindow(mWindow, SW_SHOW); | ||
3007 | } | ||
3008 | |||
3009 | |||
3010 | void LLSplashScreenWin32::updateImpl(const char *mesg) | ||
3011 | { | ||
3012 | if (!mWindow) return; | ||
3013 | |||
3014 | WCHAR w_mesg[1024]; | ||
3015 | mbstowcs(w_mesg, mesg, 1024); | ||
3016 | |||
3017 | SendDlgItemMessage(mWindow, | ||
3018 | 666, // HACK: text id | ||
3019 | WM_SETTEXT, | ||
3020 | FALSE, | ||
3021 | (LPARAM)w_mesg); | ||
3022 | } | ||
3023 | |||
3024 | |||
3025 | void LLSplashScreenWin32::hideImpl() | ||
3026 | { | ||
3027 | if (mWindow) | ||
3028 | { | ||
3029 | DestroyWindow(mWindow); | ||
3030 | mWindow = NULL; | ||
3031 | } | ||
3032 | } | ||
3033 | |||
3034 | |||
3035 | // static | ||
3036 | LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg, | ||
3037 | WPARAM w_param, LPARAM l_param) | ||
3038 | { | ||
3039 | // Just give it to windows | ||
3040 | return DefWindowProc(h_wnd, u_msg, w_param, l_param); | ||
3041 | } | ||
3042 | |||
3043 | // | ||
3044 | // Helper Funcs | ||
3045 | // | ||
3046 | |||
3047 | S32 OSMessageBoxWin32(const char* text, const char* caption, U32 type) | ||
3048 | { | ||
3049 | UINT uType; | ||
3050 | |||
3051 | switch(type) | ||
3052 | { | ||
3053 | case OSMB_OK: | ||
3054 | uType = MB_OK; | ||
3055 | break; | ||
3056 | case OSMB_OKCANCEL: | ||
3057 | uType = MB_OKCANCEL; | ||
3058 | break; | ||
3059 | case OSMB_YESNO: | ||
3060 | uType = MB_YESNO; | ||
3061 | break; | ||
3062 | default: | ||
3063 | uType = MB_OK; | ||
3064 | break; | ||
3065 | } | ||
3066 | |||
3067 | // HACK! Doesn't properly handle wide strings! | ||
3068 | int retval_win = MessageBoxA(NULL, text, caption, uType); | ||
3069 | S32 retval; | ||
3070 | |||
3071 | switch(retval_win) | ||
3072 | { | ||
3073 | case IDYES: | ||
3074 | retval = OSBTN_YES; | ||
3075 | break; | ||
3076 | case IDNO: | ||
3077 | retval = OSBTN_NO; | ||
3078 | break; | ||
3079 | case IDOK: | ||
3080 | retval = OSBTN_OK; | ||
3081 | break; | ||
3082 | case IDCANCEL: | ||
3083 | retval = OSBTN_CANCEL; | ||
3084 | break; | ||
3085 | default: | ||
3086 | retval = OSBTN_CANCEL; | ||
3087 | break; | ||
3088 | } | ||
3089 | |||
3090 | return retval; | ||
3091 | } | ||
3092 | |||
3093 | |||
3094 | void spawn_web_browser(const char* escaped_url ) | ||
3095 | { | ||
3096 | bool found = false; | ||
3097 | S32 i; | ||
3098 | for (i = 0; i < gURLProtocolWhitelistCount; i++) | ||
3099 | { | ||
3100 | S32 len = strlen(gURLProtocolWhitelist[i]); | ||
3101 | if (!strncmp(escaped_url, gURLProtocolWhitelist[i], len) | ||
3102 | && escaped_url[len] == ':') | ||
3103 | { | ||
3104 | found = true; | ||
3105 | break; | ||
3106 | } | ||
3107 | } | ||
3108 | |||
3109 | if (!found) | ||
3110 | { | ||
3111 | llwarns << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << llendl; | ||
3112 | return; | ||
3113 | } | ||
3114 | |||
3115 | llinfos << "Opening URL " << escaped_url << llendl; | ||
3116 | |||
3117 | // Figure out the user's default web browser | ||
3118 | // HKEY_CLASSES_ROOT\http\shell\open\command | ||
3119 | char reg_path_str[256]; | ||
3120 | sprintf(reg_path_str, "%s\\shell\\open\\command", gURLProtocolWhitelistHandler[i]); | ||
3121 | WCHAR reg_path_wstr[256]; | ||
3122 | mbstowcs(reg_path_wstr, reg_path_str, 1024); | ||
3123 | |||
3124 | HKEY key; | ||
3125 | WCHAR browser_open_wstr[1024]; | ||
3126 | DWORD buffer_length = 1024; | ||
3127 | RegOpenKeyEx(HKEY_CLASSES_ROOT, reg_path_wstr, 0, KEY_QUERY_VALUE, &key); | ||
3128 | RegQueryValueEx(key, NULL, NULL, NULL, (LPBYTE)browser_open_wstr, &buffer_length); | ||
3129 | RegCloseKey(key); | ||
3130 | |||
3131 | // Convert to STL string | ||
3132 | LLWString browser_open_wstring = utf16str_to_wstring(browser_open_wstr); | ||
3133 | |||
3134 | if (browser_open_wstring.length() < 2) | ||
3135 | { | ||
3136 | llwarns << "Invalid browser executable in registry " << browser_open_wstring << llendl; | ||
3137 | return; | ||
3138 | } | ||
3139 | |||
3140 | // Extract the process that's supposed to be launched | ||
3141 | LLWString browser_executable; | ||
3142 | if (browser_open_wstring[0] == '"') | ||
3143 | { | ||
3144 | // executable is quoted, find the matching quote | ||
3145 | size_t quote_pos = browser_open_wstring.find('"', 1); | ||
3146 | // copy out the string including both quotes | ||
3147 | browser_executable = browser_open_wstring.substr(0, quote_pos+1); | ||
3148 | } | ||
3149 | else | ||
3150 | { | ||
3151 | // executable not quoted, find a space | ||
3152 | size_t space_pos = browser_open_wstring.find(' ', 1); | ||
3153 | browser_executable = browser_open_wstring.substr(0, space_pos); | ||
3154 | } | ||
3155 | |||
3156 | llinfos << "Browser reg key: " << wstring_to_utf8str(browser_open_wstring) << llendl; | ||
3157 | llinfos << "Browser executable: " << wstring_to_utf8str(browser_executable) << llendl; | ||
3158 | |||
3159 | // Convert URL to wide string for Windows API | ||
3160 | // Assume URL is UTF8, as can come from scripts | ||
3161 | LLWString url_wstring = utf8str_to_wstring(escaped_url); | ||
3162 | llutf16string url_utf16 = wstring_to_utf16str(url_wstring); | ||
3163 | |||
3164 | // Convert executable and path to wide string for Windows API | ||
3165 | llutf16string browser_exec_utf16 = wstring_to_utf16str(browser_executable); | ||
3166 | |||
3167 | // ShellExecute returns HINSTANCE for backwards compatiblity. | ||
3168 | // MS docs say to cast to int and compare to 32. | ||
3169 | HWND our_window = NULL; | ||
3170 | LPCWSTR directory_wstr = NULL; | ||
3171 | int retval = (int) ShellExecute(our_window, | ||
3172 | L"open", | ||
3173 | browser_exec_utf16.c_str(), | ||
3174 | url_utf16.c_str(), | ||
3175 | directory_wstr, | ||
3176 | SW_SHOWNORMAL); | ||
3177 | if (retval > 32) | ||
3178 | { | ||
3179 | llinfos << "load_url success with " << retval << llendl; | ||
3180 | } | ||
3181 | else | ||
3182 | { | ||
3183 | llinfos << "load_url failure with " << retval << llendl; | ||
3184 | } | ||
3185 | } | ||
3186 | |||
3187 | void shell_open( const char* file_path ) | ||
3188 | { | ||
3189 | llinfos << "Opening " << file_path << llendl; | ||
3190 | |||
3191 | WCHAR wstr[1024]; | ||
3192 | mbstowcs(wstr, file_path, 1024); | ||
3193 | |||
3194 | HWND our_window = NULL; | ||
3195 | int retval = (int) ShellExecute(our_window, L"open", wstr, NULL, NULL, SW_SHOWNORMAL); | ||
3196 | if (retval > 32) | ||
3197 | { | ||
3198 | llinfos << "ShellExecute success with " << retval << llendl; | ||
3199 | } | ||
3200 | else | ||
3201 | { | ||
3202 | llinfos << "ShellExecute failure with " << retval << llendl; | ||
3203 | } | ||
3204 | } | ||
3205 | |||
3206 | BOOL LLWindowWin32::dialog_color_picker ( F32 *r, F32 *g, F32 *b ) | ||
3207 | { | ||
3208 | BOOL retval = FALSE; | ||
3209 | |||
3210 | static CHOOSECOLOR cc; | ||
3211 | static COLORREF crCustColors[16]; | ||
3212 | cc.lStructSize = sizeof(CHOOSECOLOR); | ||
3213 | cc.hwndOwner = mWindowHandle; | ||
3214 | cc.hInstance = NULL; | ||
3215 | cc.rgbResult = RGB ((*r * 255.f),(*g *255.f),(*b * 255.f)); | ||
3216 | //cc.rgbResult = RGB (0x80,0x80,0x80); | ||
3217 | cc.lpCustColors = crCustColors; | ||
3218 | cc.Flags = CC_RGBINIT | CC_FULLOPEN; | ||
3219 | cc.lCustData = 0; | ||
3220 | cc.lpfnHook = NULL; | ||
3221 | cc.lpTemplateName = NULL; | ||
3222 | |||
3223 | // This call is modal, so pause agent | ||
3224 | //send_agent_pause(); // this is in newview and we don't want to set up a dependency | ||
3225 | { | ||
3226 | retval = ChooseColor(&cc); | ||
3227 | } | ||
3228 | //send_agent_resume(); // this is in newview and we don't want to set up a dependency | ||
3229 | |||
3230 | *b = ((F32)((cc.rgbResult >> 16) & 0xff)) / 255.f; | ||
3231 | |||
3232 | *g = ((F32)((cc.rgbResult >> 8) & 0xff)) / 255.f; | ||
3233 | |||
3234 | *r = ((F32)(cc.rgbResult & 0xff)) / 255.f; | ||
3235 | |||
3236 | return (retval); | ||
3237 | } | ||
3238 | |||
3239 | void *LLWindowWin32::getPlatformWindow() | ||
3240 | { | ||
3241 | return (void*)mWindowHandle; | ||
3242 | } | ||
3243 | |||
3244 | void LLWindowWin32::bringToFront() | ||
3245 | { | ||
3246 | BringWindowToTop(mWindowHandle); | ||
3247 | } | ||
3248 | |||
3249 | // set (OS) window focus back to the client | ||
3250 | void LLWindowWin32::focusClient() | ||
3251 | { | ||
3252 | SetFocus ( mWindowHandle ); | ||
3253 | }; | ||
3254 | |||
3255 | #endif // LL_WINDOWS | ||