blob: 99013d0e01044bd493c54abd57597ec79f08503f [file] [log] [blame]
[email protected]cb3b1f9312010-06-07 19:58:231// Copyright (c) 2010 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
5#include "remoting/host/capturer_linux.h"
6
[email protected]66980d82010-10-21 20:05:017#include <X11/Xlib.h>
8#include <X11/Xutil.h>
9#include <X11/extensions/Xdamage.h>
10
11#include <set>
12
13#include "remoting/base/types.h"
14
[email protected]cb3b1f9312010-06-07 19:58:2315namespace remoting {
16
[email protected]66980d82010-10-21 20:05:0117static int IndexOfLowestBit(unsigned int mask) {
18 int i = 0;
19
20 // Extra-special do-while premature optimization, just to make dmaclach@
21 // happy.
22 do {
23 if (mask & 1) {
24 return i;
25 }
26 mask >>= 1;
27 ++i;
28 } while (mask);
29
30 NOTREACHED() << "mask should never be 0.";
31 return 0;
32}
33
34static bool IsRgb32(XImage* image) {
35 return (IndexOfLowestBit(image->red_mask) == 16) &&
36 (IndexOfLowestBit(image->green_mask) == 8) &&
37 (IndexOfLowestBit(image->blue_mask) == 0);
38}
39
40static uint32_t* GetRowStart(uint8* buffer_start, int stride, int cur_row) {
41 return reinterpret_cast<uint32_t*>(buffer_start + (cur_row * stride));
42}
43
44// Private Implementation pattern to avoid leaking the X11 types into the header
45// file.
46class CapturerLinuxPimpl {
47 public:
48 explicit CapturerLinuxPimpl(CapturerLinux* capturer);
49 ~CapturerLinuxPimpl();
50
51 bool Init(); // TODO(ajwong): Do we really want this to be synchronous?
52 void CalculateInvalidRects();
53 void CaptureRects(const InvalidRects& rects,
54 Capturer::CaptureCompletedCallback* callback);
55
56 private:
57 void DeinitXlib();
58 // We expose two forms of blitting to handle variations in the pixel format.
59 // In FastBlit, the operation is effectively a memcpy.
60 void FastBlit(XImage* image, int dest_x, int dest_y,
61 CaptureData* capture_data);
62 void SlowBlit(XImage* image, int dest_x, int dest_y,
63 CaptureData* capture_data);
64
65 static const int kBytesPerPixel = 4;
66
67 // Reference to containing class so we can access friend functions.
68 CapturerLinux* capturer_;
69
70 // X11 graphics context.
71 Display* display_;
72 GC gc_;
73 Window root_window_;
74
75 // XDamage information.
76 Damage damage_handle_;
77 int damage_event_base_;
78 int damage_error_base_;
79
80 // Capture state.
81 uint8* buffers_[CapturerLinux::kNumBuffers];
82 int stride_;
83 bool capture_fullscreen_;
84};
85
86CapturerLinux::CapturerLinux()
87 : pimpl_(new CapturerLinuxPimpl(this)) {
88 // TODO(ajwong): This should be moved into an Init() method on Capturer
89 // itself. Then we can remove the CHECK.
90 CHECK(pimpl_->Init());
[email protected]cb3b1f9312010-06-07 19:58:2391}
92
93CapturerLinux::~CapturerLinux() {
94}
95
[email protected]804c99622010-07-01 15:02:5396void CapturerLinux::ScreenConfigurationChanged() {
[email protected]66980d82010-10-21 20:05:0197 // TODO(ajwong): Support resolution changes.
98 NOTIMPLEMENTED();
[email protected]cb3b1f9312010-06-07 19:58:2399}
100
[email protected]88552a92010-08-06 22:50:00101void CapturerLinux::CalculateInvalidRects() {
[email protected]66980d82010-10-21 20:05:01102 pimpl_->CalculateInvalidRects();
[email protected]88552a92010-08-06 22:50:00103}
104
105void CapturerLinux::CaptureRects(const InvalidRects& rects,
[email protected]804c99622010-07-01 15:02:53106 CaptureCompletedCallback* callback) {
[email protected]66980d82010-10-21 20:05:01107 pimpl_->CaptureRects(rects, callback);
108}
109
110CapturerLinuxPimpl::CapturerLinuxPimpl(CapturerLinux* capturer)
111 : capturer_(capturer),
112 display_(NULL),
113 gc_(NULL),
114 root_window_(BadValue),
115 damage_handle_(BadValue),
116 damage_event_base_(-1),
117 damage_error_base_(-1),
118 stride_(0),
119 capture_fullscreen_(true) {
120 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
121 buffers_[i] = NULL;
122 }
123}
124
125CapturerLinuxPimpl::~CapturerLinuxPimpl() {
126 DeinitXlib();
127
128 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
129 delete [] buffers_[i];
130 buffers_[i] = NULL;
131 }
132}
133
134bool CapturerLinuxPimpl::Init() {
135 // TODO(ajwong): We should specify the display string we are attaching to
136 // in the constructor.
137 display_ = XOpenDisplay(NULL);
138 if (!display_) {
139 LOG(ERROR) << "Unable to open display";
140 return false;
141 }
142
143 root_window_ = RootWindow(display_, DefaultScreen(display_));
144 if (root_window_ == BadValue) {
145 LOG(ERROR) << "Unable to get the root window";
146 DeinitXlib();
147 return false;
148 }
149
150 gc_ = XCreateGC(display_, root_window_, 0, NULL);
151 if (gc_ == NULL) {
152 LOG(ERROR) << "Unable to get graphics context";
153 DeinitXlib();
154 return false;
155 }
156
157 // Setup XDamage to report changes in the damage window. Mark the whole
158 // window as invalid.
159 if (!XDamageQueryExtension(display_, &damage_event_base_,
160 &damage_error_base_)) {
161 LOG(ERROR) << "Server does not support XDamage.";
162 DeinitXlib();
163 return false;
164 }
165 damage_handle_ = XDamageCreate(display_, root_window_,
166 XDamageReportDeltaRectangles);
167 if (damage_handle_ == BadValue) {
168 LOG(ERROR) << "Unable to create damage handle.";
169 DeinitXlib();
170 return false;
171 }
172
173 // TODO(ajwong): We should be able to replace this with a XDamageAdd().
174 capture_fullscreen_ = true;
175
176 // Set up the dimensions of the catpure framebuffer.
177 XWindowAttributes root_attr;
178 XGetWindowAttributes(display_, root_window_, &root_attr);
179 capturer_->width_ = root_attr.width;
180 capturer_->height_ = root_attr.height;
181 stride_ = capturer_->width() * kBytesPerPixel;
182 VLOG(1) << "Initialized with Geometry: " << capturer_->width()
183 << "x" << capturer_->height();
184
185 // Allocate the screen buffers.
186 for (int i = 0; i < CapturerLinux::kNumBuffers; i++) {
187 buffers_[i] =
188 new uint8[capturer_->width() * capturer_->height() * kBytesPerPixel];
189 }
190
191 return true;
192}
193
194void CapturerLinuxPimpl::CalculateInvalidRects() {
195 // TODO(ajwong): The capture_fullscreen_ logic here is very ugly. Refactor.
196
197 // Find the number of events that are outstanding "now." We don't just loop
198 // on XPending because we want to guarantee this terminates.
199 int events_to_process = XPending(display_);
200 XEvent e;
201 InvalidRects invalid_rects;
202 for (int i = 0; i < events_to_process; i++) {
203 XNextEvent(display_, &e);
204 if (e.type == damage_event_base_ + XDamageNotify) {
205 // If we're doing a full screen capture, we should just drain the events.
206 if (!capture_fullscreen_) {
207 XDamageNotifyEvent *event = reinterpret_cast<XDamageNotifyEvent*>(&e);
208 gfx::Rect damage_rect(event->area.x, event->area.y, event->area.width,
209 event->area.height);
210 invalid_rects.insert(damage_rect);
211 VLOG(3) << "Damage receved for rect at ("
212 << damage_rect.x() << "," << damage_rect.y() << ") size ("
213 << damage_rect.width() << "," << damage_rect.height() << ")";
214 }
215 } else {
216 LOG(WARNING) << "Got unknown event type: " << e.type;
217 }
218 }
219
220 if (capture_fullscreen_) {
221 capturer_->InvalidateFullScreen();
222 capture_fullscreen_ = false;
223 } else {
224 capturer_->InvalidateRects(invalid_rects);
225 }
226}
227
228void CapturerLinuxPimpl::CaptureRects(
229 const InvalidRects& rects,
230 Capturer::CaptureCompletedCallback* callback) {
231 uint8* buffer = buffers_[capturer_->current_buffer_];
232 DataPlanes planes;
233 planes.data[0] = buffer;
234 planes.strides[0] = stride_;
235
236 scoped_refptr<CaptureData> capture_data(
237 new CaptureData(planes, capturer_->width(), capturer_->height(),
[email protected]04b36142010-11-02 01:08:19238 PIXEL_FORMAT_RGB32));
[email protected]66980d82010-10-21 20:05:01239
240 for (InvalidRects::const_iterator it = rects.begin();
241 it != rects.end();
242 ++it) {
243 XImage* image = XGetImage(display_, root_window_, it->x(), it->y(),
244 it->width(), it->height(), AllPlanes, ZPixmap);
245
246 // Check if we can fastpath the blit.
247 if ((image->depth == 24 || image->depth == 32) &&
248 image->bits_per_pixel == 32 &&
249 IsRgb32(image)) {
250 VLOG(3) << "Fast blitting";
251 FastBlit(image, it->x(), it->y(), capture_data);
252 } else {
253 VLOG(3) << "Slow blitting";
254 SlowBlit(image, it->x(), it->y(), capture_data);
255 }
256
257 XDestroyImage(image);
258 }
259
260 // TODO(ajwong): We should only repair the rects that were copied!
261 XDamageSubtract(display_, damage_handle_, None, None);
262
263 capture_data->mutable_dirty_rects() = rects;
264 // TODO(ajwong): These completion signals back to the upper class are very
265 // strange. Fix it.
266 capturer_->FinishCapture(capture_data, callback);
267}
268
269void CapturerLinuxPimpl::DeinitXlib() {
270 if (gc_) {
271 XFreeGC(display_, gc_);
272 gc_ = NULL;
273 }
274
275 if (display_) {
276 XCloseDisplay(display_);
277 display_ = NULL;
278 }
279}
280
281void CapturerLinuxPimpl::FastBlit(XImage* image, int dest_x, int dest_y,
282 CaptureData* capture_data) {
283 uint8* src_pos = reinterpret_cast<uint8*>(image->data);
284
285 DataPlanes planes = capture_data->data_planes();
286 uint8* dst_buffer = planes.data[0];
287
288 const int dst_stride = planes.strides[0];
289 const int src_stride = image->bytes_per_line;
290
291 // TODO(ajwong): I think this can never happen anyways due to the way we size
292 // the buffer. Check to be certain and possibly remove check.
293 CHECK((dst_stride - dest_x) >= src_stride);
294
295 // Flip the coordinate system to match the client.
296 for (int y = image->height - 1; y >= 0; y--) {
297 uint32_t* dst_pos = GetRowStart(dst_buffer, dst_stride, y + dest_y);
298 dst_pos += dest_x;
299
300 memcpy(dst_pos, src_pos, src_stride);
301
302 src_pos += src_stride;
303 }
304}
305
306void CapturerLinuxPimpl::SlowBlit(XImage* image, int dest_x, int dest_y,
307 CaptureData* capture_data) {
308 DataPlanes planes = capture_data->data_planes();
309 uint8* dst_buffer = planes.data[0];
310 const int dst_stride = planes.strides[0];
311
312 unsigned int red_shift = IndexOfLowestBit(image->red_mask);
313 unsigned int blue_shift = IndexOfLowestBit(image->blue_mask);
314 unsigned int green_shift = IndexOfLowestBit(image->green_mask);
315
316 unsigned int max_red = image->red_mask >> red_shift;
317 unsigned int max_blue = image->blue_mask >> blue_shift;
318 unsigned int max_green = image->green_mask >> green_shift;
319
320 for (int y = 0; y < image->height; y++) {
321 // Flip the coordinate system to match the client.
322 int dst_row = image->height - y - 1;
323 uint32_t* dst_pos = GetRowStart(dst_buffer, dst_stride, dst_row + dest_y);
324 dst_pos += dest_x;
325 for (int x = 0; x < image->width; x++) {
326 unsigned long pixel = XGetPixel(image, x, y);
327 uint32_t r = (((pixel & image->red_mask) >> red_shift) * max_red) / 255;
328 uint32_t g =
329 (((pixel & image->green_mask) >> green_shift) * max_blue) / 255;
330 uint32_t b =
331 (((pixel & image->blue_mask) >> blue_shift) * max_green) / 255;
332
333 // Write as 32-bit RGB.
334 *dst_pos = r << 16 | g << 8 | b;
335 dst_pos++;
336 }
337 }
[email protected]cb3b1f9312010-06-07 19:58:23338}
339
340} // namespace remoting