Index: plugin/win/main_win.cc |
=================================================================== |
--- plugin/win/main_win.cc (revision 26426) |
+++ plugin/win/main_win.cc (working copy) |
@@ -87,7 +87,7 @@ |
#endif // O3D_INTERNAL_PLUGIN |
namespace { |
-const wchar_t* const kFullScreenWindowClassName = L"O3DFullScreenWindowClass"; |
+const wchar_t* const kO3DWindowClassName = L"O3DWindowClass"; |
// We would normally make this a stack variable in main(), but in a |
// plugin, that's not possible, so we make it a global. When the DLL is loaded |
@@ -352,79 +352,6 @@ |
} |
} |
-// This returns 0 on success, 1 on failure, to match WindowProc. |
-LRESULT ForwardEvent(PluginObject *obj, |
- HWND hWnd, |
- UINT Msg, |
- WPARAM wParam, |
- LPARAM lParam, |
- bool translateCoords) { |
- DCHECK(obj); |
- DCHECK(obj->GetPluginHWnd()); |
- HWND dest_hwnd = obj->GetParentHWnd(); |
- DCHECK(hWnd); |
- DCHECK(dest_hwnd); |
- bool fullscreen = hWnd == obj->GetFullscreenHWnd(); |
- if (fullscreen) { |
- dest_hwnd = obj->GetPluginHWnd(); |
- } else if (obj->IsChrome()) { |
- // When trying to find the parent window of the Chrome plugin, new Chrome is |
- // different than old Chrome; it's got an extra wrapper window around the |
- // plugin that didn't used to be there. The wrapper won't listen to events, |
- // so if we see it, we have to go one window up the tree from there in order |
- // to find someone who'll listen to us. The new behavior is seen in nightly |
- // builds of Chromium as of 2.0.163.0 (9877) [but went in some time before |
- // that]; the old behavior is still exhibited by Chrome as of 1.0.154.48. |
- wchar_t chrome_class_name[] = L"WrapperNativeWindowClass"; |
- wchar_t buffer[sizeof(chrome_class_name) / sizeof(chrome_class_name[0])]; |
- if (!GetClassName(dest_hwnd, buffer, sizeof(buffer) / sizeof(buffer[0]))) { |
- return 1; |
- } |
- if (!wcscmp(chrome_class_name, buffer)) { |
- dest_hwnd = ::GetParent(dest_hwnd); |
- } |
- } |
- if (translateCoords) { |
- int x = GET_X_LPARAM(lParam); |
- int y = GET_Y_LPARAM(lParam); |
- |
- RECT rect0, rect1; |
- if (!::GetWindowRect(hWnd, &rect0)) { |
- DCHECK(false); |
- return 1; |
- } |
- if (!::GetWindowRect(dest_hwnd, &rect1)) { |
- DCHECK(false); |
- return 1; |
- } |
- int width = rect0.right - rect0.left; |
- int width_1 = rect1.right - rect1.left; |
- |
- int x_1; |
- int y_1; |
- |
- if (!fullscreen) { // Translate from plugin to browser offset coords. |
- x_1 = x - rect1.left + rect0.left; |
- } else { // Translate from screen to plugin offset coords. |
- // The plugin and the fullscreen window each fill their respective entire |
- // window, so there aren't any offsets to add or subtract. |
- x_1 = x * width_1 / width; |
- } |
- int height = rect0.bottom - rect0.top; |
- int height_1 = rect1.bottom - rect1.top; |
- if (!fullscreen) { // Translate from plugin to browser offset coords. |
- y_1 = y - rect1.top + rect0.top; |
- } else { // Translate from screen to plugin offset coords. |
- // The plugin and the fullscreen window each fill their respective entire |
- // window, so there aren't any offsets to add or subtract. |
- y_1 = y * height_1 / height; |
- } |
- |
- lParam = MAKELPARAM(x_1, y_1); |
- } |
- return !::PostMessage(dest_hwnd, Msg, wParam, lParam); |
-} |
- |
LRESULT HandleDragAndDrop(PluginObject *obj, WPARAM wParam) { |
HDROP hDrop = reinterpret_cast<HDROP>(wParam); |
UINT num_files = ::DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); |
@@ -507,11 +434,10 @@ |
if (!obj->RecordPaint()) { |
::SetPixelV(hdc, 0, 0, RGB(0, 0, 0)); |
} |
- |
obj->renderer()->set_need_to_render(true); |
} else { |
- // If there Client has no Renderer associated with it, paint the draw |
- // area gray. |
+ // If the Client has no Renderer associated with it, paint the |
+ // draw area gray. |
::SelectObject(paint_struct.hdc, GetStockObject(DKGRAY_BRUSH)); |
::Rectangle(paint_struct.hdc, |
paint_struct.rcPaint.left, |
@@ -521,29 +447,17 @@ |
} |
} |
::EndPaint(hWnd, &paint_struct); |
- break; |
+ return 0; |
} |
case WM_SETCURSOR: { |
obj->set_cursor(obj->cursor()); |
return 1; |
} |
case WM_ERASEBKGND: { |
- return 1; // tell windows we don't need the background cleared |
+ // Tell windows we don't need the background cleared. |
+ return 1; |
} |
- case WM_SIZE: { |
- // Resize event called |
- if (reentrance_count.get() > 1) { |
- break; // Ignore this message; we're reentrant. |
- } |
- // get new dimensions of window |
- int window_width = LOWORD(lParam); |
- int window_height = HIWORD(lParam); |
- |
- // Tell the plugin that it has been resized |
- obj->Resize(window_width, window_height); |
- break; |
- } |
case WM_TIMER: { |
if (reentrance_count.get() > 1) { |
break; // Ignore this message; we're reentrant. |
@@ -619,20 +533,28 @@ |
case WM_SYSKEYUP: |
return HandleKeyboardEvent(obj, hWnd, Msg, wParam, lParam); |
-#if(_WIN32_WINNT >= 0x0500) |
- case WM_APPCOMMAND: |
-#endif /* _WIN32_WINNT >= 0x0500 */ |
- return ForwardEvent(obj, hWnd, Msg, wParam, lParam, false); |
- |
case WM_DROPFILES: |
return HandleDragAndDrop(obj, wParam); |
+ case WM_ACTIVATE: |
+ // We don't receive WM_KILLFOCUS when Alt-Tabbing away from a |
+ // full-screen window. We do however get WM_ACTIVATE. |
+ if (LOWORD(wParam) == WA_INACTIVE) { |
+ if (obj->fullscreen()) { |
+ obj->CancelFullscreenDisplay(); |
+ } |
+ } |
+ return 0; |
+ |
case WM_KILLFOCUS: |
// If we lose focus [which also happens on alt+f4 killing the fullscreen |
// window] fall back to plugin mode to avoid lost-device awkwardness. |
// TODO: We'll have problems with this when dealing with e.g. |
// Japanese text input IME windows. |
- if (hWnd == obj->GetFullscreenHWnd()) { |
+ if (obj->fullscreen()) { |
+ // TODO(kbr): consider doing this somehow more asynchronously; |
+ // not supposed to cause window activation in the WM_KILLFOCUS |
+ // handler |
obj->CancelFullscreenDisplay(); |
return 0; |
} |
@@ -645,34 +567,63 @@ |
// manually, its destructor will know not to. |
reentrance_count.decrement(); |
- if (hWnd == obj->GetFullscreenHWnd()) { |
- return ::CallWindowProc(::DefWindowProc, |
- hWnd, |
- Msg, |
- wParam, |
- lParam); |
- } else { |
- return ::CallWindowProc(obj->GetDefaultPluginWindowProc(), |
- hWnd, |
- Msg, |
- wParam, |
- lParam); |
- } |
+ return ::CallWindowProc(::DefWindowProc, |
+ hWnd, |
+ Msg, |
+ wParam, |
+ lParam); |
} |
return 0; |
} |
-bool RegisterFullScreenWindowClass() { |
- WNDCLASSEX window_class = { sizeof(WNDCLASSEX) }; |
+static const wchar_t* kOrigWndProcName = L"o3dOrigWndProc"; |
+ |
+LRESULT CALLBACK PluginWindowInterposer(HWND hWnd, |
+ UINT Msg, |
+ WPARAM wParam, |
+ LPARAM lParam) { |
+ switch (Msg) { |
+ case WM_PAINT: { |
+ // For nicer startup appearance, allow the browser to paint the |
+ // plugin window until we start to draw 3D content. Forbid the |
+ // browser from painting once we have started to draw to prevent |
+ // a flash in Firefox upon our receiving focus the first time. |
+ PluginObject *obj = PluginObject::GetPluginProperty(hWnd); |
+ if (obj != NULL && obj->renderer() != NULL) { |
+ if (obj->renderer()->presented_once()) { |
+ // Tell Windows we painted the window region. |
+ ::ValidateRect(hWnd, NULL); |
+ return 0; |
+ } |
+ } |
+ // Break out to call the original window procedure to paint the |
+ // window. |
+ break; |
+ } |
+ |
+ default: |
+ break; |
+ } |
+ |
+ WNDPROC proc = static_cast<WNDPROC>(GetProp(hWnd, kOrigWndProcName)); |
+ DCHECK(proc != NULL); |
+ return CallWindowProc(proc, hWnd, Msg, wParam, lParam); |
+} |
+ |
+bool RegisterO3DWindowClass() { |
+ WNDCLASSEX window_class; |
+ ZeroMemory(&window_class, sizeof(window_class)); |
+ window_class.cbSize = sizeof(window_class); |
window_class.hInstance = g_module_instance; |
window_class.lpfnWndProc = WindowProc; |
- window_class.lpszClassName = kFullScreenWindowClassName; |
- window_class.style = CS_DBLCLKS; |
+ window_class.lpszClassName = kO3DWindowClassName; |
+ // We use CS_OWNDC in case we are rendering OpenGL into this window. |
+ window_class.style = CS_DBLCLKS | CS_OWNDC; |
return RegisterClassEx(&window_class) != 0; |
} |
-void UnregisterFullScreenWindowClass() { |
- UnregisterClass(kFullScreenWindowClassName, g_module_instance); |
+void UnregisterO3DWindowClass() { |
+ UnregisterClass(kO3DWindowClassName, g_module_instance); |
} |
NPError InitializePlugin() { |
@@ -696,58 +647,41 @@ |
DLOG(INFO) << "NP_Initialize"; |
- if (!RegisterFullScreenWindowClass()) |
+ if (!RegisterO3DWindowClass()) |
return NPERR_MODULE_LOAD_FAILED_ERROR; |
return NPERR_NO_ERROR; |
} |
-void CleanupFullscreenWindow(PluginObject *obj) { |
- DCHECK(obj->GetFullscreenHWnd()); |
- obj->StorePluginProperty(obj->GetPluginHWnd(), obj); |
- ::DestroyWindow(obj->GetFullscreenHWnd()); |
- obj->SetFullscreenHWnd(NULL); |
-} |
- |
void CleanupAllWindows(PluginObject *obj) { |
- DCHECK(obj->GetHWnd()); |
+ DCHECK(obj->GetContentHWnd()); |
DCHECK(obj->GetPluginHWnd()); |
- ::KillTimer(obj->GetHWnd(), 0); |
- if (obj->GetFullscreenHWnd()) { |
- CleanupFullscreenWindow(obj); |
- } |
- PluginObject::ClearPluginProperty(obj->GetHWnd()); |
- ::SetWindowLongPtr(obj->GetPluginHWnd(), |
- GWL_WNDPROC, |
- reinterpret_cast<LONG_PTR>( |
- obj->GetDefaultPluginWindowProc())); |
+ ::KillTimer(obj->GetContentHWnd(), 0); |
+ PluginObject::ClearPluginProperty(obj->GetContentHWnd()); |
+ PluginObject::ClearPluginProperty(obj->GetPluginHWnd()); |
+ ::DestroyWindow(obj->GetContentHWnd()); |
+ obj->SetContentHWnd(NULL); |
obj->SetPluginHWnd(NULL); |
obj->SetHWnd(NULL); |
} |
-HWND CreateFullscreenWindow(PluginObject *obj, |
- int mode_id) { |
- o3d::DisplayMode mode; |
- if (!obj->renderer()->GetDisplayMode(mode_id, &mode)) { |
- return NULL; |
- } |
- CHECK(mode.width() > 0 && mode.height() > 0); |
- |
- HWND hWnd = CreateWindowEx(NULL, |
- kFullScreenWindowClassName, |
- L"O3D Test Fullscreen Window", |
- WS_POPUP, |
- 0, 0, |
- mode.width(), |
- mode.height(), |
- NULL, |
- NULL, |
- g_module_instance, |
- NULL); |
- |
- ShowWindow(hWnd, SW_SHOW); |
- return hWnd; |
+// Re-parents the content_hwnd into the containing_hwnd, resizing the |
+// content_hwnd to the given width and height in the process. |
+void ReplaceContentWindow(HWND content_hwnd, |
+ HWND containing_hwnd, |
+ int width, int height) { |
+ ::ShowWindow(content_hwnd, SW_HIDE); |
+ LONG_PTR style = ::GetWindowLongPtr(content_hwnd, GWL_STYLE); |
+ style |= WS_CHILD; |
+ ::SetWindowLongPtr(content_hwnd, GWL_STYLE, style); |
+ ::SetParent(content_hwnd, containing_hwnd); |
+ BOOL res = ::SetWindowPos(content_hwnd, containing_hwnd, |
+ 0, 0, width, height, |
+ SWP_NOZORDER | SWP_ASYNCWINDOWPOS); |
+ DCHECK(res); |
+ ::ShowWindow(content_hwnd, SW_SHOW); |
} |
+ |
} // namespace anonymous |
#if defined(O3D_INTERNAL_PLUGIN) |
@@ -767,7 +701,7 @@ |
HANDLE_CRASHES; |
DLOG(INFO) << "NP_Shutdown"; |
- UnregisterFullScreenWindowClass(); |
+ UnregisterO3DWindowClass(); |
#if !defined(O3D_INTERNAL_PLUGIN) |
@@ -870,31 +804,63 @@ |
} |
return NPERR_NO_ERROR; |
} |
- if (obj->GetHWnd() == hWnd) { |
- return NPERR_NO_ERROR; |
- } |
- if (obj->fullscreen()) { |
- // We can get here if the user alt+tabs away from the fullscreen plugin |
- // window or JavaScript resizes the plugin window. |
- DCHECK(obj->GetPluginHWnd()); |
- DCHECK(obj->GetFullscreenHWnd()); |
- DCHECK(obj->GetPluginHWnd() == hWnd); |
- // Exit full screen if the plugin window is being modified. |
- obj->CancelFullscreenDisplay(); |
- |
+ if (obj->GetPluginHWnd() == hWnd) { |
+ // May need to resize the content window. |
+ DCHECK(obj->GetContentHWnd()); |
+ // Avoid spurious resize requests. |
+ if (window->width != obj->width() || |
+ window->height != obj->height()) { |
+ if (!obj->fullscreen()) { |
+ ::SetWindowPos(obj->GetContentHWnd(), obj->GetPluginHWnd(), 0, 0, |
+ window->width, window->height, |
+ SWP_NOZORDER | SWP_NOREPOSITION); |
+ } |
+ // Even if we are in full-screen mode, store off the new width |
+ // and height to restore to them later. |
+ obj->Resize(window->width, window->height); |
+ // Only propagate this resize event to the client if it isn't in |
+ // full-screen mode. |
+ if (!obj->fullscreen()) { |
+ obj->client()->SendResizeEvent(obj->width(), obj->height(), false); |
+ } |
+ } |
return NPERR_NO_ERROR; |
} |
+ |
DCHECK(!obj->GetPluginHWnd()); |
obj->SetPluginHWnd(hWnd); |
- obj->SetParentHWnd(::GetParent(hWnd)); |
- PluginObject::StorePluginProperty(hWnd, obj); |
- obj->SetDefaultPluginWindowProc( |
- reinterpret_cast<WNDPROC>( |
- ::SetWindowLongPtr(hWnd, |
- GWL_WNDPROC, |
- reinterpret_cast<LONG_PTR>(WindowProc)))); |
+ // Subclass the plugin window's window procedure to avoid processing |
+ // WM_PAINT. This seems to only be necessary for Firefox, which |
+ // overdraws our plugin the first time it gains focus. |
+ SetProp(hWnd, kOrigWndProcName, |
+ reinterpret_cast<HANDLE>(GetWindowLongPtr(hWnd, GWLP_WNDPROC))); |
+ PluginObject::StorePluginPropertyUnsafe(hWnd, obj); |
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, |
+ reinterpret_cast<LONG_PTR>(PluginWindowInterposer)); |
+ |
+ // Create the content window, into which O3D always renders, rather |
+ // than alternating rendering between the browser's window and a |
+ // separate full-screen window. The O3D window is removed from the |
+ // browser's hierarchy and made top-level in order to go to |
+ // full-screen mode via Direct3D. This solves fundamental focus |
+ // fighting problems seen on Windows Vista. |
+ HWND content_window = |
+ CreateWindow(kO3DWindowClassName, |
+ L"O3D Window", |
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, |
+ 0, 0, |
+ window->width, window->height, |
+ hWnd, |
+ NULL, |
+ g_module_instance, |
+ NULL); |
+ obj->Resize(window->width, window->height); |
+ obj->SetContentHWnd(content_window); |
+ PluginObject::StorePluginProperty(content_window, obj); |
+ ::ShowWindow(content_window, SW_SHOW); |
+ |
// create and assign the graphics context |
DisplayWindowWindows default_display; |
default_display.set_hwnd(obj->GetHWnd()); |
@@ -937,33 +903,44 @@ |
bool PluginObject::RequestFullscreenDisplay() { |
bool success = false; |
DCHECK(GetPluginHWnd()); |
+ DCHECK(GetContentHWnd()); |
if (!fullscreen_ && renderer_ && fullscreen_region_valid_) { |
DCHECK(renderer_->fullscreen() == fullscreen_); |
- DCHECK(!GetFullscreenHWnd()); |
- HWND drawing_hwnd = |
- CreateFullscreenWindow(this, fullscreen_region_mode_id_); |
- if (drawing_hwnd) { |
- ::KillTimer(GetHWnd(), 0); |
- SetFullscreenHWnd(drawing_hwnd); |
- StorePluginPropertyUnsafe(drawing_hwnd, this); |
- |
+ // The focus window we pass into IDirect3D9::CreateDevice must not |
+ // fight with the full-screen window for the focus. The best way |
+ // to achieve this is to re-use the content window for full-screen |
+ // mode. |
+ ::ShowWindow(GetContentHWnd(), SW_HIDE); |
+ ::SetParent(GetContentHWnd(), NULL); |
+ // Remove WS_CHILD from the window style |
+ LONG_PTR style = ::GetWindowLongPtr(GetContentHWnd(), GWL_STYLE); |
+ style &= ~WS_CHILD; |
+ ::SetWindowLongPtr(GetContentHWnd(), GWL_STYLE, style); |
+ ::ShowWindow(GetContentHWnd(), SW_SHOW); |
+ // We need to resize the full-screen window to the desired size of |
+ // the display mode early, before calling |
+ // Renderer::GoFullscreen(). |
+ o3d::DisplayMode mode; |
+ if (GetDisplayMode(fullscreen_region_mode_id_, &mode)) { |
+ ::SetWindowPos(GetContentHWnd(), HWND_TOP, 0, 0, |
+ mode.width(), mode.height(), |
+ SWP_NOZORDER | SWP_NOREPOSITION | SWP_ASYNCWINDOWPOS); |
DisplayWindowWindows display; |
- display.set_hwnd(GetHWnd()); |
- if (renderer_->SetFullscreen(true, display, |
- fullscreen_region_mode_id_)) { |
+ display.set_hwnd(GetContentHWnd()); |
+ if (renderer_->GoFullscreen(display, |
+ fullscreen_region_mode_id_)) { |
fullscreen_ = true; |
client()->SendResizeEvent(renderer_->width(), renderer_->height(), |
- true); |
+ true); |
success = true; |
- } else { |
- CleanupFullscreenWindow(this); |
} |
- prev_width_ = renderer_->width(); |
- prev_height_ = renderer_->height(); |
- ::SetTimer(GetHWnd(), 0, 10, NULL); |
- } else { |
- LOG(ERROR) << "Failed to create fullscreen window."; |
} |
+ |
+ if (!success) { |
+ ReplaceContentWindow(GetContentHWnd(), GetPluginHWnd(), |
+ prev_width_, prev_height_); |
+ LOG(ERROR) << "Failed to switch to fullscreen mode."; |
+ } |
} |
return success; |
} |
@@ -973,18 +950,15 @@ |
if (fullscreen_) { |
DCHECK(renderer()); |
DCHECK(renderer()->fullscreen()); |
- ::KillTimer(GetHWnd(), 0); |
+ fullscreen_ = false; |
DisplayWindowWindows display; |
- display.set_hwnd(GetPluginHWnd()); |
- if (!renderer_->SetFullscreen(false, display, 0)) { |
+ display.set_hwnd(GetContentHWnd()); |
+ if (!renderer_->CancelFullscreen(display, prev_width_, prev_height_)) { |
LOG(FATAL) << "Failed to get the renderer out of fullscreen mode!"; |
} |
- CleanupFullscreenWindow(this); |
- prev_width_ = renderer_->width(); |
- prev_height_ = renderer_->height(); |
+ ReplaceContentWindow(GetContentHWnd(), GetPluginHWnd(), |
+ prev_width_, prev_height_); |
client()->SendResizeEvent(prev_width_, prev_height_, false); |
- ::SetTimer(GetHWnd(), 0, 10, NULL); |
- fullscreen_ = false; |
} |
} |
} // namespace _o3d |