aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llwindow/llwindowwin32.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llwindow/llwindowwin32.cpp')
-rw-r--r--linden/indra/llwindow/llwindowwin32.cpp3255
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
56const S32 WM_MOUSEWHEEL = 0x020A;
57const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
58const S32 MAX_MESSAGE_PER_UPDATE = 20;
59const S32 BITS_PER_PIXEL = 32;
60const S32 MAX_NUM_RESOLUTIONS = 32;
61const F32 ICON_FLASH_TIME = 0.5f;
62
63extern BOOL gDebugWindowProc;
64
65LPWSTR gIconResource = IDI_APPLICATION;
66
67LLW32MsgCallback gAsyncMsgCallback = NULL;
68
69//
70// LLWindowWin32
71//
72
73void 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
97BOOL 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
131BOOL LLWindowWin32::sIsClassRegistered = FALSE;
132
133
134
135LPDIRECTINPUT8 g_pDI = NULL;
136LPDIRECTINPUTDEVICE8 g_pJoystick = NULL;
137BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
138 VOID* pContext );
139BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
140 VOID* pContext );
141
142
143LLWindowWin32::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
852LLWindowWin32::~LLWindowWin32()
853{
854 delete [] mWindowTitle;
855 mWindowTitle = NULL;
856
857 delete [] mSupportedResolutions;
858 mSupportedResolutions = NULL;
859
860 delete mWindowClassName;
861 mWindowClassName = NULL;
862}
863
864void LLWindowWin32::show()
865{
866 ShowWindow(mWindowHandle, SW_SHOW);
867 SetForegroundWindow(mWindowHandle);
868 SetFocus(mWindowHandle);
869}
870
871void LLWindowWin32::hide()
872{
873 setMouseClipping(FALSE);
874 ShowWindow(mWindowHandle, SW_HIDE);
875}
876
877void LLWindowWin32::minimize()
878{
879 setMouseClipping(FALSE);
880 showCursor();
881 ShowWindow(mWindowHandle, SW_MINIMIZE);
882}
883
884
885void 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()
895void 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
960BOOL LLWindowWin32::isValid()
961{
962 return (mWindowHandle != NULL);
963}
964
965BOOL LLWindowWin32::getVisible()
966{
967 return (mWindowHandle && IsWindowVisible(mWindowHandle));
968}
969
970BOOL LLWindowWin32::getMinimized()
971{
972 return (mWindowHandle && IsIconic(mWindowHandle));
973}
974
975BOOL LLWindowWin32::getMaximized()
976{
977 return (mWindowHandle && IsZoomed(mWindowHandle));
978}
979
980BOOL 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
997BOOL LLWindowWin32::getFullscreen()
998{
999 return mFullscreen;
1000}
1001
1002BOOL 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
1018BOOL 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
1034BOOL 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
1050BOOL 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
1063BOOL 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
1078BOOL 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
1559void 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
1573BOOL 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
1591BOOL 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
1608void 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
1618void 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
1629void LLWindowWin32::showCursorFromMouseMove()
1630{
1631 if (!mHideCursorPermanent)
1632 {
1633 showCursor();
1634 }
1635}
1636
1637void LLWindowWin32::hideCursorUntilMouseMove()
1638{
1639 if (!mHideCursorPermanent)
1640 {
1641 hideCursor();
1642 mHideCursorPermanent = FALSE;
1643 }
1644}
1645
1646BOOL LLWindowWin32::isCursorHidden()
1647{
1648 return mCursorHidden;
1649}
1650
1651
1652HCURSOR 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
1663void 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
1716void 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
1731ECursorType LLWindowWin32::getCursor()
1732{
1733 return mCurrentCursor;
1734}
1735
1736void LLWindowWin32::captureMouse()
1737{
1738 SetCapture(mWindowHandle);
1739}
1740
1741void LLWindowWin32::releaseMouse()
1742{
1743 ReleaseCapture();
1744}
1745
1746
1747void LLWindowWin32::delayInputProcessing()
1748{
1749 mInputProcessingPaused = TRUE;
1750}
1751
1752void 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
1801LRESULT 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
2279BOOL 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
2299BOOL 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
2318BOOL 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
2335BOOL 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
2352BOOL 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
2366BOOL 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
2381BOOL LLWindowWin32::isClipboardTextAvailable()
2382{
2383 return IsClipboardFormatAvailable(CF_UNICODETEXT) || IsClipboardFormatAvailable( CF_TEXT );
2384}
2385
2386
2387BOOL 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
2436BOOL 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.
2498void 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
2528BOOL 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
2558BOOL 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
2631S32 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
2637void 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
2649F32 LLWindowWin32::getGamma()
2650{
2651 return mCurrentGamma;
2652}
2653
2654BOOL LLWindowWin32::restoreGamma()
2655{
2656 return SetDeviceGammaRamp(mhDC, mPrevGammaRamp);
2657}
2658
2659BOOL 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
2682LLWindow::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
2725F32 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
2745F32 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
2760BOOL 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
2802BOOL 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
2815BOOL 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
2833void LLWindowWin32::swapBuffers()
2834{
2835 SwapBuffers(mhDC);
2836}
2837
2838
2839BOOL 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
2857BOOL 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
2878void 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//
2988LLSplashScreenWin32::LLSplashScreenWin32()
2989: mWindow(NULL)
2990{
2991}
2992
2993LLSplashScreenWin32::~LLSplashScreenWin32()
2994{
2995}
2996
2997void 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
3010void 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
3025void LLSplashScreenWin32::hideImpl()
3026{
3027 if (mWindow)
3028 {
3029 DestroyWindow(mWindow);
3030 mWindow = NULL;
3031 }
3032}
3033
3034
3035// static
3036LRESULT 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
3047S32 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
3094void 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
3187void 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
3206BOOL 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
3239void *LLWindowWin32::getPlatformWindow()
3240{
3241 return (void*)mWindowHandle;
3242}
3243
3244void LLWindowWin32::bringToFront()
3245{
3246 BringWindowToTop(mWindowHandle);
3247}
3248
3249// set (OS) window focus back to the client
3250void LLWindowWin32::focusClient()
3251{
3252 SetFocus ( mWindowHandle );
3253};
3254
3255#endif // LL_WINDOWS