1/*
2 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/test/linux/glx_renderer.h"
12
13#include <assert.h>
14
15#include <X11/Xatom.h>
16#include <X11/Xlib.h>
17
18#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
19
20namespace webrtc {
21namespace test {
22
23GlxRenderer::GlxRenderer(size_t width, size_t height)
24    : width_(width),
25      height_(height),
26      display_(NULL),
27      context_(NULL) {
28  assert(width > 0);
29  assert(height > 0);
30}
31
32GlxRenderer::~GlxRenderer() { Destroy(); }
33
34bool GlxRenderer::Init(const char* window_title) {
35  if ((display_ = XOpenDisplay(NULL)) == NULL) {
36    Destroy();
37    return false;
38  }
39
40  int screen = DefaultScreen(display_);
41
42  XVisualInfo* vi;
43  int attr_list[] = { GLX_DOUBLEBUFFER, GLX_RGBA, GLX_RED_SIZE, 4,
44                      GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 16,
45                      None, };
46
47  if ((vi = glXChooseVisual(display_, screen, attr_list)) == NULL) {
48    Destroy();
49    return false;
50  }
51
52  context_ = glXCreateContext(display_, vi, 0, true);
53  if (context_ == NULL) {
54    Destroy();
55    return false;
56  }
57
58  XSetWindowAttributes window_attributes;
59  window_attributes.colormap = XCreateColormap(
60      display_, RootWindow(display_, vi->screen), vi->visual, AllocNone);
61  window_attributes.border_pixel = 0;
62  window_attributes.event_mask = StructureNotifyMask | ExposureMask;
63  window_ = XCreateWindow(display_, RootWindow(display_, vi->screen), 0, 0,
64                          width_, height_, 0, vi->depth, InputOutput,
65                          vi->visual, CWBorderPixel | CWColormap | CWEventMask,
66                          &window_attributes);
67  XFree(vi);
68
69  XSetStandardProperties(display_, window_, window_title, window_title, None,
70                         NULL, 0, NULL);
71
72  Atom wm_delete = XInternAtom(display_, "WM_DELETE_WINDOW", True);
73  if (wm_delete != None) {
74    XSetWMProtocols(display_, window_, &wm_delete, 1);
75  }
76
77  XMapRaised(display_, window_);
78
79  if (!glXMakeCurrent(display_, window_, context_)) {
80    Destroy();
81    return false;
82  }
83  GlRenderer::Init();
84  if (!glXMakeCurrent(display_, None, NULL)) {
85    Destroy();
86    return false;
87  }
88
89  Resize(width_, height_);
90  return true;
91}
92
93void GlxRenderer::Destroy() {
94  if (context_ != NULL) {
95    glXMakeCurrent(display_, window_, context_);
96    GlRenderer::Destroy();
97    glXMakeCurrent(display_, None, NULL);
98    glXDestroyContext(display_, context_);
99    context_ = NULL;
100  }
101
102  if (display_ != NULL) {
103    XCloseDisplay(display_);
104    display_ = NULL;
105  }
106}
107
108GlxRenderer* GlxRenderer::Create(const char* window_title, size_t width,
109                                 size_t height) {
110  GlxRenderer* glx_renderer = new GlxRenderer(width, height);
111  if (!glx_renderer->Init(window_title)) {
112    // TODO(pbos): Add GLX-failed warning here?
113    delete glx_renderer;
114    return NULL;
115  }
116  return glx_renderer;
117}
118
119void GlxRenderer::Resize(size_t width, size_t height) {
120  width_ = width;
121  height_ = height;
122  if (!glXMakeCurrent(display_, window_, context_)) {
123    abort();
124  }
125  GlRenderer::ResizeViewport(width_, height_);
126  if (!glXMakeCurrent(display_, None, NULL)) {
127    abort();
128  }
129
130  XSizeHints* size_hints = XAllocSizeHints();
131  if (size_hints == NULL) {
132    abort();
133  }
134  size_hints->flags = PAspect;
135  size_hints->min_aspect.x = size_hints->max_aspect.x = width_;
136  size_hints->min_aspect.y = size_hints->max_aspect.y = height_;
137  XSetWMNormalHints(display_, window_, size_hints);
138  XFree(size_hints);
139
140  XWindowChanges wc;
141  wc.width = static_cast<int>(width);
142  wc.height = static_cast<int>(height);
143  XConfigureWindow(display_, window_, CWWidth | CWHeight, &wc);
144}
145
146void GlxRenderer::RenderFrame(const webrtc::I420VideoFrame& frame,
147                              int /*render_delay_ms*/) {
148  if (static_cast<size_t>(frame.width()) != width_ ||
149      static_cast<size_t>(frame.height()) != height_) {
150    Resize(static_cast<size_t>(frame.width()),
151           static_cast<size_t>(frame.height()));
152  }
153
154  XEvent event;
155  if (!glXMakeCurrent(display_, window_, context_)) {
156    abort();
157  }
158  while (XPending(display_)) {
159    XNextEvent(display_, &event);
160    switch (event.type) {
161      case ConfigureNotify:
162        GlRenderer::ResizeViewport(event.xconfigure.width,
163                                   event.xconfigure.height);
164        break;
165      default:
166        break;
167    }
168  }
169
170  GlRenderer::RenderFrame(frame, 0);
171  glXSwapBuffers(display_, window_);
172
173  if (!glXMakeCurrent(display_, None, NULL)) {
174    abort();
175  }
176}
177}  // test
178}  // webrtc
179