| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/client/x11_view.h" | 5 #include "remoting/client/x11_view.h" |
| 6 | 6 |
| 7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
| 8 #include <X11/Xutil.h> | 8 #include <X11/Xutil.h> |
| 9 #include <X11/extensions/Xrender.h> | 9 #include <X11/extensions/Xrender.h> |
| 10 #include <X11/extensions/Xcomposite.h> | 10 #include <X11/extensions/Xcomposite.h> |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "remoting/base/decoder_zlib.h" | 13 #include "base/task.h" |
| 14 | 14 |
| 15 namespace remoting { | 15 namespace remoting { |
| 16 | 16 |
| 17 X11View::X11View() | 17 X11View::X11View() |
| 18 : display_(NULL), | 18 : display_(NULL), |
| 19 window_(0), | 19 window_(0), |
| 20 picture_(0) { | 20 picture_(0) { |
| 21 } | 21 } |
| 22 | 22 |
| 23 X11View::~X11View() { | 23 X11View::~X11View() { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 if (display_ && window_) { | 58 if (display_ && window_) { |
| 59 // Shutdown the window system. | 59 // Shutdown the window system. |
| 60 XDestroyWindow(display_, window_); | 60 XDestroyWindow(display_, window_); |
| 61 XCloseDisplay(display_); | 61 XCloseDisplay(display_); |
| 62 } | 62 } |
| 63 display_ = NULL; | 63 display_ = NULL; |
| 64 window_ = 0; | 64 window_ = 0; |
| 65 } | 65 } |
| 66 | 66 |
| 67 void X11View::Paint() { | 67 void X11View::Paint() { |
| 68 NOTIMPLEMENTED() << "Not sure if we need this call anymore."; |
| 69 } |
| 70 |
| 71 void X11View::PaintRect(media::VideoFrame* frame, const gfx::Rect& clip) { |
| 68 // Don't bother attempting to paint if the display hasn't been set up. | 72 // Don't bother attempting to paint if the display hasn't been set up. |
| 69 if (!display_ || !window_ || !frame_height_ || !frame_width_ || !frame_) { | 73 if (!display_ || !window_ || !frame) { |
| 70 return; | 74 return; |
| 71 } | 75 } |
| 72 | 76 |
| 73 // TODO(hclam): Paint only the updated regions. | |
| 74 all_update_rects_.clear(); | |
| 75 | |
| 76 // If we have not initialized the render target then do it now. | 77 // If we have not initialized the render target then do it now. |
| 77 if (!picture_) | 78 if (!picture_) |
| 78 InitPaintTarget(); | 79 InitPaintTarget(); |
| 79 | 80 |
| 80 // Upload the image to a pixmap. And then create a picture from the pixmap | 81 // Upload the image to a pixmap. And then create a picture from the pixmap |
| 81 // and composite the picture over the picture representing the window. | 82 // and composite the picture over the picture representing the window. |
| 82 | 83 |
| 83 // Creates a XImage. | 84 // Creates a XImage. |
| 84 XImage image; | 85 XImage image; |
| 85 memset(&image, 0, sizeof(image)); | 86 memset(&image, 0, sizeof(image)); |
| 86 image.width = frame_width_; | 87 image.width = frame->width(); |
| 87 image.height = frame_height_; | 88 image.height = frame->height(); |
| 88 image.depth = 32; | 89 image.depth = 32; |
| 89 image.bits_per_pixel = 32; | 90 image.bits_per_pixel = 32; |
| 90 image.format = ZPixmap; | 91 image.format = ZPixmap; |
| 91 image.byte_order = LSBFirst; | 92 image.byte_order = LSBFirst; |
| 92 image.bitmap_unit = 8; | 93 image.bitmap_unit = 8; |
| 93 image.bitmap_bit_order = LSBFirst; | 94 image.bitmap_bit_order = LSBFirst; |
| 94 image.bytes_per_line = frame_->stride(media::VideoFrame::kRGBPlane); | 95 image.bytes_per_line = frame_->stride(media::VideoFrame::kRGBPlane); |
| 95 image.red_mask = 0xff; | 96 image.red_mask = 0xff; |
| 96 image.green_mask = 0xff00; | 97 image.green_mask = 0xff00; |
| 97 image.blue_mask = 0xff0000; | 98 image.blue_mask = 0xff0000; |
| 98 image.data = reinterpret_cast<char*>( | 99 image.data = reinterpret_cast<char*>( |
| 99 frame_->data(media::VideoFrame::kRGBPlane)); | 100 frame_->data(media::VideoFrame::kRGBPlane)); |
| 100 | 101 |
| 101 // Creates a pixmap and uploads from the XImage. | 102 // Creates a pixmap and uploads from the XImage. |
| 102 unsigned long pixmap = XCreatePixmap(display_, window_, | 103 unsigned long pixmap = XCreatePixmap(display_, window_, |
| 103 frame_width_, frame_height_, 32); | 104 frame->width(), frame->height(), 32); |
| 104 | 105 |
| 105 GC gc = XCreateGC(display_, pixmap, 0, NULL); | 106 GC gc = XCreateGC(display_, pixmap, 0, NULL); |
| 106 XPutImage(display_, pixmap, gc, &image, 0, 0, 0, 0, | 107 XPutImage(display_, pixmap, gc, &image, clip.x(), clip.y(), |
| 107 frame_width_, frame_height_); | 108 clip.x(), clip.y(), clip.width(), clip.height()); |
| 108 XFreeGC(display_, gc); | 109 XFreeGC(display_, gc); |
| 109 | 110 |
| 110 // Creates the picture representing the pixmap. | 111 // Creates the picture representing the pixmap. |
| 111 XID picture = XRenderCreatePicture( | 112 XID picture = XRenderCreatePicture( |
| 112 display_, pixmap, | 113 display_, pixmap, |
| 113 XRenderFindStandardFormat(display_, PictStandardARGB32), | 114 XRenderFindStandardFormat(display_, PictStandardARGB32), |
| 114 0, NULL); | 115 0, NULL); |
| 115 | 116 |
| 116 // Composite the picture over the picture representing the window. | 117 // Composite the picture over the picture representing the window. |
| 117 XRenderComposite(display_, PictOpSrc, picture, 0, | 118 XRenderComposite(display_, PictOpSrc, picture, 0, |
| 118 picture_, 0, 0, 0, 0, 0, 0, | 119 picture_, 0, 0, 0, 0, clip.x(), clip.y(), |
| 119 frame_width_, frame_height_); | 120 clip.width(), clip.height()); |
| 120 | 121 |
| 121 XRenderFreePicture(display_, picture); | 122 XRenderFreePicture(display_, picture); |
| 122 XFreePixmap(display_, pixmap); | 123 XFreePixmap(display_, pixmap); |
| 123 } | 124 } |
| 124 | 125 |
| 125 void X11View::SetSolidFill(uint32 color) { | 126 void X11View::SetSolidFill(uint32 color) { |
| 126 // TODO(garykac): Implement. | 127 // TODO(garykac): Implement. |
| 127 // NOTIMPLEMENTED(); | 128 // NOTIMPLEMENTED(); |
| 128 } | 129 } |
| 129 | 130 |
| 130 void X11View::UnsetSolidFill() { | 131 void X11View::UnsetSolidFill() { |
| 131 // TODO(garykac): Implement. | 132 // TODO(garykac): Implement. |
| 132 // NOTIMPLEMENTED(); | 133 // NOTIMPLEMENTED(); |
| 133 } | 134 } |
| 134 | 135 |
| 135 void X11View::SetViewport(int x, int y, int width, int height) { | 136 void X11View::SetViewport(int x, int y, int width, int height) { |
| 136 // TODO(garykac): Implement. | 137 // TODO(garykac): Implement. |
| 137 // NOTIMPLEMENTED(); | 138 // NOTIMPLEMENTED(); |
| 138 } | 139 } |
| 139 | 140 |
| 140 void X11View::SetHostScreenSize(int width, int height) { | |
| 141 frame_width_ = width; | |
| 142 frame_height_ = height; | |
| 143 frame_ = NULL; | |
| 144 | |
| 145 XResizeWindow(display_, window_, frame_width_, frame_height_); | |
| 146 } | |
| 147 | |
| 148 void X11View::InitPaintTarget() { | 141 void X11View::InitPaintTarget() { |
| 149 // Testing XRender support. | 142 // Testing XRender support. |
| 150 int dummy; | 143 int dummy; |
| 151 bool xrender_support = XRenderQueryExtension(display_, &dummy, &dummy); | 144 bool xrender_support = XRenderQueryExtension(display_, &dummy, &dummy); |
| 152 CHECK(xrender_support) << "XRender is not supported!"; | 145 CHECK(xrender_support) << "XRender is not supported!"; |
| 153 | 146 |
| 154 XWindowAttributes attr; | 147 XWindowAttributes attr; |
| 155 XGetWindowAttributes(display_, window_, &attr); | 148 XGetWindowAttributes(display_, window_, &attr); |
| 156 | 149 |
| 157 XRenderPictFormat* pictformat = XRenderFindVisualFormat( | 150 XRenderPictFormat* pictformat = XRenderFindVisualFormat( |
| 158 display_, | 151 display_, |
| 159 attr.visual); | 152 attr.visual); |
| 160 CHECK(pictformat) << "XRENDER does not support default visual"; | 153 CHECK(pictformat) << "XRENDER does not support default visual"; |
| 161 | 154 |
| 162 picture_ = XRenderCreatePicture(display_, window_, pictformat, 0, NULL); | 155 picture_ = XRenderCreatePicture(display_, window_, pictformat, 0, NULL); |
| 163 CHECK(picture_) << "Backing picture not created"; | 156 CHECK(picture_) << "Backing picture not created"; |
| 164 } | 157 } |
| 165 | 158 |
| 166 void X11View::HandleBeginUpdateStream(ChromotingHostMessage* msg) { | 159 void X11View::AllocateFrame(media::VideoFrame::Format format, |
| 167 scoped_ptr<ChromotingHostMessage> deleter(msg); | 160 size_t width, |
| 161 size_t height, |
| 162 base::TimeDelta timestamp, |
| 163 base::TimeDelta duration, |
| 164 scoped_refptr<media::VideoFrame>* frame_out, |
| 165 Task* done) { |
| 166 // TODO(ajwong): Implement this to use the native X window rather than |
| 167 // just a generic frame buffer. |
| 168 media::VideoFrame::CreateFrame(media::VideoFrame::RGB32, |
| 169 width, height, |
| 170 base::TimeDelta(), base::TimeDelta(), |
| 171 frame_out); |
| 172 if (*frame_out) { |
| 173 (*frame_out)->AddRef(); |
| 174 } |
| 175 done->Run(); |
| 176 delete done; |
| 177 } |
| 168 | 178 |
| 169 // Make sure the |frame_| is initialized. | 179 void X11View::ReleaseFrame(media::VideoFrame* frame) { |
| 170 if (!frame_) { | 180 if (frame) { |
| 171 media::VideoFrame::CreateFrame(media::VideoFrame::RGB32, | 181 LOG(WARNING) << "Frame released."; |
| 172 frame_width_, frame_height_, | 182 frame->Release(); |
| 173 base::TimeDelta(), base::TimeDelta(), | |
| 174 &frame_); | |
| 175 CHECK(frame_); | |
| 176 } | 183 } |
| 177 } | 184 } |
| 178 | 185 |
| 179 void X11View::HandleUpdateStreamPacket(ChromotingHostMessage* msg) { | 186 void X11View::OnPartialFrameOutput(media::VideoFrame* frame, |
| 180 // Lazily initialize the decoder. | 187 UpdatedRects* rects, |
| 181 SetupDecoder(msg->update_stream_packet().begin_rect().encoding()); | 188 Task* done) { |
| 182 if (!decoder_->IsStarted()) { | |
| 183 BeginDecoding(NewRunnableMethod(this, &X11View::OnPartialDecodeDone), | |
| 184 NewRunnableMethod(this, &X11View::OnDecodeDone)); | |
| 185 } | |
| 186 | |
| 187 Decode(msg); | |
| 188 } | |
| 189 | |
| 190 void X11View::HandleEndUpdateStream(ChromotingHostMessage* msg) { | |
| 191 scoped_ptr<ChromotingHostMessage> deleter(msg); | |
| 192 EndDecoding(); | |
| 193 } | |
| 194 | |
| 195 void X11View::OnPartialDecodeDone() { | |
| 196 // Decoder has produced some output so schedule a paint. We'll get a Paint() | |
| 197 // call in the near future. Note that we can get UpdateStreamPacket during | |
| 198 // this short period of time and we will perform decode again and the | |
| 199 // information in updated rects will be lost. | |
| 200 // There are several ways to solve this problem. | |
| 201 // 1. Merge the updated rects and perform one paint. | |
| 202 // 2. Queue the updated rects and perform two paints. | |
| 203 // 3. Ignore the updated rects and always paint the full image. Since we | |
| 204 // use one frame as output this will always be correct. | |
| 205 // We will take (1) and simply concat the list of rectangles. | |
| 206 all_update_rects_.insert(all_update_rects_.begin() + | |
| 207 all_update_rects_.size(), | |
| 208 update_rects_.begin(), update_rects_.end()); | |
| 209 | |
| 210 // TODO(hclam): Make sure we call this method on the right thread. Since | 189 // TODO(hclam): Make sure we call this method on the right thread. Since |
| 211 // decoder is single-threaded we don't have a problem but we better post | 190 // decoder is single-threaded we don't have a problem but we better post |
| 212 // a task to do the right thing. | 191 // a task to do the right thing. |
| 192 |
| 193 for (UpdatedRects::iterator it = rects->begin(); it != rects->end(); ++it) { |
| 194 PaintRect(frame, *it); |
| 195 } |
| 196 |
| 197 // TODO(ajwong): Shouldn't we only expose the part of the window that was |
| 198 // damanged? |
| 213 XEvent event; | 199 XEvent event; |
| 214 event.type = Expose; | 200 event.type = Expose; |
| 215 XSendEvent(display_, static_cast<int>(window_), true, ExposureMask, &event); | 201 XSendEvent(display_, static_cast<int>(window_), true, ExposureMask, &event); |
| 216 } | |
| 217 | 202 |
| 218 void X11View::OnDecodeDone() { | 203 done->Run(); |
| 219 // Since we do synchronous decoding here there's nothing in this method. | 204 delete done; |
| 220 } | 205 } |
| 221 | 206 |
| 222 } // namespace remoting | 207 } // namespace remoting |
| OLD | NEW |