1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5extern "C" {
6#include <X11/extensions/Xcomposite.h>
7}
8
9#include "ui/gl/gl_image_glx.h"
10
11#include "base/basictypes.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/message_loop/message_loop.h"
15#include "ui/gfx/x/x11_types.h"
16#include "ui/gl/gl_bindings.h"
17#include "ui/gl/gl_surface_glx.h"
18
19namespace gfx {
20
21namespace {
22
23// scoped_ptr functor for XFree(). Use as follows:
24//   scoped_ptr<XVisualInfo, ScopedPtrXFree> foo(...);
25// where "XVisualInfo" is any X type that is freed with XFree.
26struct ScopedPtrXFree {
27  void operator()(void* x) const { ::XFree(x); }
28};
29
30int BindToTextureFormat(int depth) {
31  if (depth == 32)
32    return GLX_BIND_TO_TEXTURE_RGBA_EXT;
33
34  return GLX_BIND_TO_TEXTURE_RGB_EXT;
35}
36
37int TextureFormat(int depth) {
38  if (depth == 32)
39    return GLX_TEXTURE_FORMAT_RGBA_EXT;
40
41  return GLX_TEXTURE_FORMAT_RGB_EXT;
42}
43
44}  // namespace anonymous
45
46GLImageGLX::GLImageGLX(gfx::PluginWindowHandle window)
47    : display_(gfx::GetXDisplay()),
48      window_(window),
49      pixmap_(0),
50      glx_pixmap_(0) {}
51
52GLImageGLX::~GLImageGLX() { Destroy(); }
53
54bool GLImageGLX::Initialize() {
55  if (!GLSurfaceGLX::IsTextureFromPixmapSupported()) {
56    LOG(ERROR) << "GLX_EXT_texture_from_pixmap not supported.";
57    return false;
58  }
59
60  XWindowAttributes attributes;
61  if (!XGetWindowAttributes(display_, window_, &attributes)) {
62    LOG(ERROR) << "XGetWindowAttributes failed for window " << window_ << ".";
63    return false;
64  }
65
66  XVisualInfo templ;
67  templ.visualid = XVisualIDFromVisual(attributes.visual);
68  int num_visinfo = 0;
69  scoped_ptr<XVisualInfo, ScopedPtrXFree> visinfo(
70      XGetVisualInfo(display_, VisualIDMask, &templ, &num_visinfo));
71  if (!visinfo.get()) {
72    LOG(ERROR) << "XGetVisualInfo failed for visual id " << templ.visualid
73               << ".";
74    return false;
75  }
76  if (!num_visinfo) {
77    LOG(ERROR) << "XGetVisualInfo returned 0 elements.";
78    return false;
79  }
80
81  int config_attribs[] = {
82      static_cast<int>(GLX_VISUAL_ID),     static_cast<int>(visinfo->visualid),
83      GLX_DRAWABLE_TYPE,                   GLX_PIXMAP_BIT,
84      GLX_BIND_TO_TEXTURE_TARGETS_EXT,     GLX_TEXTURE_2D_EXT,
85      BindToTextureFormat(visinfo->depth), GL_TRUE,
86      0};
87  int num_elements = 0;
88  scoped_ptr<GLXFBConfig, ScopedPtrXFree> config(glXChooseFBConfig(
89      display_, DefaultScreen(display_), config_attribs, &num_elements));
90  if (!config.get()) {
91    LOG(ERROR) << "glXChooseFBConfig failed.";
92    return false;
93  }
94  if (!num_elements) {
95    LOG(ERROR) << "glXChooseFBConfig returned 0 elements.";
96    return false;
97  }
98
99  // Create backing pixmap reference.
100  pixmap_ = XCompositeNameWindowPixmap(display_, window_);
101
102  XID root = 0;
103  int x = 0;
104  int y = 0;
105  unsigned int width = 0;
106  unsigned int height = 0;
107  unsigned int bw = 0;
108  unsigned int depth = 0;
109  if (!XGetGeometry(
110          display_, pixmap_, &root, &x, &y, &width, &height, &bw, &depth)) {
111    LOG(ERROR) << "XGetGeometry failed for pixmap " << pixmap_ << ".";
112    return false;
113  }
114
115  int pixmap_attribs[] = {GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
116                          GLX_TEXTURE_FORMAT_EXT, TextureFormat(visinfo->depth),
117                          0};
118  glx_pixmap_ =
119      glXCreatePixmap(display_, *config.get(), pixmap_, pixmap_attribs);
120  if (!glx_pixmap_) {
121    LOG(ERROR) << "glXCreatePixmap failed.";
122    return false;
123  }
124
125  size_ = gfx::Size(width, height);
126  return true;
127}
128
129void GLImageGLX::Destroy() {
130  if (glx_pixmap_) {
131    glXDestroyGLXPixmap(display_, glx_pixmap_);
132    glx_pixmap_ = 0;
133  }
134  if (pixmap_) {
135    XFreePixmap(display_, pixmap_);
136    pixmap_ = 0;
137  }
138}
139
140gfx::Size GLImageGLX::GetSize() { return size_; }
141
142bool GLImageGLX::BindTexImage(unsigned target) {
143  if (!glx_pixmap_)
144    return false;
145
146  // Requires TEXTURE_2D target.
147  if (target != GL_TEXTURE_2D)
148    return false;
149
150  glXBindTexImageEXT(display_, glx_pixmap_, GLX_FRONT_LEFT_EXT, 0);
151  return true;
152}
153
154void GLImageGLX::ReleaseTexImage(unsigned target) {
155  DCHECK(glx_pixmap_);
156  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), target);
157
158  glXReleaseTexImageEXT(display_, glx_pixmap_, GLX_FRONT_LEFT_EXT);
159}
160
161}  // namespace gfx
162