14a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com// Copyright (c) 2012 The Chromium Authors. All rights reserved.
24a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com// Use of this source code is governed by a BSD-style license that can be
34a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com// found in the LICENSE file.
44a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com
54a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_surface_wgl.h"
64a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com
74a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "base/debug/trace_event.h"
84a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "base/logging.h"
94a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "base/memory/scoped_ptr.h"
104a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_bindings.h"
114a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_gl_api_implementation.h"
124a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com#include "ui/gl/gl_wgl_api_implementation.h"
134a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com
144a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comnamespace gfx {
154a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com
164a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comnamespace {
174a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comconst PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = {
184a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  sizeof(kPixelFormatDescriptor),    // Size of structure.
194a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  1,                       // Default version.
204a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  PFD_DRAW_TO_WINDOW |     // Window drawing support.
214a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  PFD_SUPPORT_OPENGL |     // OpenGL support.
224a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  PFD_DOUBLEBUFFER,        // Double buffering support (not stereo).
234a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  PFD_TYPE_RGBA,           // RGBA color mode (not indexed).
244a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  24,                      // 24 bit color mode.
254a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0, 0, 0, 0, 0, 0,        // Don't set RGB bits & shifts.
264a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  8, 0,                    // 8 bit alpha
274a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0,                       // No accumulation buffer.
284a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0, 0, 0, 0,              // Ignore accumulation bits.
294a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0,                       // no z-buffer.
304a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0,                       // no stencil buffer.
314a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0,                       // No aux buffer.
324a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  PFD_MAIN_PLANE,          // Main drawing plane (not overlay).
334a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0,                       // Reserved.
344a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  0, 0, 0,                 // Layer masks ignored.
354a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com};
364a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com
374a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.comLRESULT CALLBACK IntermediateWindowProc(HWND window,
384a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com                                        UINT message,
394a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com                                        WPARAM w_param,
404a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com                                        LPARAM l_param) {
414a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com  switch (message) {
424a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com    case WM_ERASEBKGND:
434a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com      // Prevent windows from erasing the background.
444a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com      return 1;
454a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com    case WM_PAINT:
464a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com      // Do not paint anything.
474a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com      PAINTSTRUCT paint;
484a6c3279070e8f133607a74c08d8c08ac394ab98erik.corry@gmail.com      if (BeginPaint(window, &paint))
49        EndPaint(window, &paint);
50      return 0;
51    default:
52      return DefWindowProc(window, message, w_param, l_param);
53  }
54}
55
56class DisplayWGL {
57 public:
58  DisplayWGL()
59      : module_handle_(0),
60        window_class_(0),
61        window_handle_(0),
62        device_context_(0),
63        pixel_format_(0) {
64  }
65
66  ~DisplayWGL() {
67    if (window_handle_)
68      DestroyWindow(window_handle_);
69    if (window_class_)
70      UnregisterClass(reinterpret_cast<wchar_t*>(window_class_),
71                      module_handle_);
72  }
73
74  bool Init() {
75    // We must initialize a GL context before we can bind to extension entry
76    // points. This requires the device context for a window.
77    if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT |
78                           GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
79                           reinterpret_cast<wchar_t*>(IntermediateWindowProc),
80                           &module_handle_)) {
81      LOG(ERROR) << "GetModuleHandleEx failed.";
82      return false;
83    }
84
85    WNDCLASS intermediate_class;
86    intermediate_class.style = CS_OWNDC;
87    intermediate_class.lpfnWndProc = IntermediateWindowProc;
88    intermediate_class.cbClsExtra = 0;
89    intermediate_class.cbWndExtra = 0;
90    intermediate_class.hInstance = module_handle_;
91    intermediate_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
92    intermediate_class.hCursor = LoadCursor(NULL, IDC_ARROW);
93    intermediate_class.hbrBackground = NULL;
94    intermediate_class.lpszMenuName = NULL;
95    intermediate_class.lpszClassName = L"Intermediate GL Window";
96    window_class_ = RegisterClass(&intermediate_class);
97    if (!window_class_) {
98      LOG(ERROR) << "RegisterClass failed.";
99      return false;
100    }
101
102    window_handle_ = CreateWindow(
103        reinterpret_cast<wchar_t*>(window_class_),
104        L"",
105        WS_OVERLAPPEDWINDOW,
106        0, 0,
107        100, 100,
108        NULL,
109        NULL,
110        NULL,
111        NULL);
112    if (!window_handle_) {
113      LOG(ERROR) << "CreateWindow failed.";
114      return false;
115    }
116
117    device_context_ = GetDC(window_handle_);
118    pixel_format_ = ChoosePixelFormat(device_context_,
119                                      &kPixelFormatDescriptor);
120    if (pixel_format_ == 0) {
121      LOG(ERROR) << "Unable to get the pixel format for GL context.";
122      return false;
123    }
124    if (!SetPixelFormat(device_context_,
125                        pixel_format_,
126                        &kPixelFormatDescriptor)) {
127      LOG(ERROR) << "Unable to set the pixel format for temporary GL context.";
128      return false;
129    }
130
131    return true;
132  }
133
134  ATOM window_class() const { return window_class_; }
135  HDC device_context() const { return device_context_; }
136  int pixel_format() const { return pixel_format_; }
137
138 private:
139  HINSTANCE module_handle_;
140  ATOM window_class_;
141  HWND window_handle_;
142  HDC device_context_;
143  int pixel_format_;
144};
145DisplayWGL* g_display;
146}  // namespace
147
148GLSurfaceWGL::GLSurfaceWGL() {
149}
150
151GLSurfaceWGL::~GLSurfaceWGL() {
152}
153
154void* GLSurfaceWGL::GetDisplay() {
155  return GetDisplayDC();
156}
157
158bool GLSurfaceWGL::InitializeOneOff() {
159  static bool initialized = false;
160  if (initialized)
161    return true;
162
163  DCHECK(g_display == NULL);
164  scoped_ptr<DisplayWGL> wgl_display(new DisplayWGL);
165  if (!wgl_display->Init())
166    return false;
167
168  g_display = wgl_display.release();
169  initialized = true;
170  return true;
171}
172
173HDC GLSurfaceWGL::GetDisplayDC() {
174  return g_display->device_context();
175}
176
177NativeViewGLSurfaceWGL::NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window)
178    : window_(window),
179      child_window_(NULL),
180      device_context_(NULL) {
181  DCHECK(window);
182}
183
184NativeViewGLSurfaceWGL::~NativeViewGLSurfaceWGL() {
185  Destroy();
186}
187
188bool NativeViewGLSurfaceWGL::Initialize() {
189  DCHECK(!device_context_);
190
191  RECT rect;
192  if (!GetClientRect(window_, &rect)) {
193    LOG(ERROR) << "GetClientRect failed.\n";
194    Destroy();
195    return false;
196  }
197
198  // Create a child window. WGL has problems using a window handle owned by
199  // another process.
200  child_window_ = CreateWindow(
201      reinterpret_cast<wchar_t*>(g_display->window_class()),
202      L"",
203      WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE,
204      0, 0,
205      rect.right - rect.left,
206      rect.bottom - rect.top,
207      window_,
208      NULL,
209      NULL,
210      NULL);
211  if (!child_window_) {
212    LOG(ERROR) << "CreateWindow failed.\n";
213    Destroy();
214    return false;
215  }
216
217  // The GL context will render to this window.
218  device_context_ = GetDC(child_window_);
219  if (!device_context_) {
220    LOG(ERROR) << "Unable to get device context for window.";
221    Destroy();
222    return false;
223  }
224
225  if (!SetPixelFormat(device_context_,
226                      g_display->pixel_format(),
227                      &kPixelFormatDescriptor)) {
228    LOG(ERROR) << "Unable to set the pixel format for GL context.";
229    Destroy();
230    return false;
231  }
232
233  return true;
234}
235
236void NativeViewGLSurfaceWGL::Destroy() {
237  if (child_window_ && device_context_)
238    ReleaseDC(child_window_, device_context_);
239
240  if (child_window_)
241    DestroyWindow(child_window_);
242
243  child_window_ = NULL;
244  device_context_ = NULL;
245}
246
247bool NativeViewGLSurfaceWGL::IsOffscreen() {
248  return false;
249}
250
251bool NativeViewGLSurfaceWGL::SwapBuffers() {
252  TRACE_EVENT2("gpu", "NativeViewGLSurfaceWGL:RealSwapBuffers",
253      "width", GetSize().width(),
254      "height", GetSize().height());
255
256  // Resize the child window to match the parent before swapping. Do not repaint
257  // it as it moves.
258  RECT rect;
259  if (!GetClientRect(window_, &rect))
260    return false;
261  if (!MoveWindow(child_window_,
262                  0, 0,
263                  rect.right - rect.left,
264                  rect.bottom - rect.top,
265                  FALSE)) {
266    return false;
267  }
268
269  DCHECK(device_context_);
270  return ::SwapBuffers(device_context_) == TRUE;
271}
272
273gfx::Size NativeViewGLSurfaceWGL::GetSize() {
274  RECT rect;
275  BOOL result = GetClientRect(child_window_, &rect);
276  DCHECK(result);
277  return gfx::Size(rect.right - rect.left, rect.bottom - rect.top);
278}
279
280void* NativeViewGLSurfaceWGL::GetHandle() {
281  return device_context_;
282}
283
284PbufferGLSurfaceWGL::PbufferGLSurfaceWGL(const gfx::Size& size)
285    : size_(size),
286      device_context_(NULL),
287      pbuffer_(NULL) {
288  // Some implementations of Pbuffer do not support having a 0 size. For such
289  // cases use a (1, 1) surface.
290  if (size_.GetArea() == 0)
291    size_.SetSize(1, 1);
292}
293
294PbufferGLSurfaceWGL::~PbufferGLSurfaceWGL() {
295  Destroy();
296}
297
298bool PbufferGLSurfaceWGL::Initialize() {
299  DCHECK(!device_context_);
300
301  if (!gfx::g_driver_wgl.fn.wglCreatePbufferARBFn) {
302    LOG(ERROR) << "wglCreatePbufferARB not available.";
303    Destroy();
304    return false;
305  }
306
307  const int kNoAttributes[] = { 0 };
308  pbuffer_ = wglCreatePbufferARB(g_display->device_context(),
309                                 g_display->pixel_format(),
310                                 size_.width(), size_.height(),
311                                 kNoAttributes);
312
313  if (!pbuffer_) {
314    LOG(ERROR) << "Unable to create pbuffer.";
315    Destroy();
316    return false;
317  }
318
319  device_context_ = wglGetPbufferDCARB(static_cast<HPBUFFERARB>(pbuffer_));
320  if (!device_context_) {
321    LOG(ERROR) << "Unable to get pbuffer device context.";
322    Destroy();
323    return false;
324  }
325
326  return true;
327}
328
329void PbufferGLSurfaceWGL::Destroy() {
330  if (pbuffer_ && device_context_)
331    wglReleasePbufferDCARB(static_cast<HPBUFFERARB>(pbuffer_), device_context_);
332
333  device_context_ = NULL;
334
335  if (pbuffer_) {
336    wglDestroyPbufferARB(static_cast<HPBUFFERARB>(pbuffer_));
337    pbuffer_ = NULL;
338  }
339}
340
341bool PbufferGLSurfaceWGL::IsOffscreen() {
342  return true;
343}
344
345bool PbufferGLSurfaceWGL::SwapBuffers() {
346  NOTREACHED() << "Attempted to call SwapBuffers on a pbuffer.";
347  return false;
348}
349
350gfx::Size PbufferGLSurfaceWGL::GetSize() {
351  return size_;
352}
353
354void* PbufferGLSurfaceWGL::GetHandle() {
355  return device_context_;
356}
357
358}  // namespace gfx
359