| OLD | NEW |
| (Empty) |
| 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 | |
| 5 #include "remoting/capturer/linux/x_server_pixel_buffer.h" | |
| 6 | |
| 7 #include <sys/shm.h> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 | |
| 11 #if defined(TOOLKIT_GTK) | |
| 12 #include <gdk/gdk.h> | |
| 13 #else // !defined(TOOLKIT_GTK) | |
| 14 #include <X11/Xlib.h> | |
| 15 #endif // !defined(TOOLKIT_GTK) | |
| 16 | |
| 17 namespace { | |
| 18 | |
| 19 #if defined(TOOLKIT_GTK) | |
| 20 // GDK sets error handler for Xlib errors, so we need to use it to | |
| 21 // trap X errors when this code is compiled with GTK. | |
| 22 void EnableXServerErrorTrap() { | |
| 23 gdk_error_trap_push(); | |
| 24 } | |
| 25 | |
| 26 int GetLastXServerError() { | |
| 27 return gdk_error_trap_pop(); | |
| 28 } | |
| 29 | |
| 30 #else // !defined(TOOLKIT_GTK) | |
| 31 | |
| 32 static bool g_xserver_error_trap_enabled = false; | |
| 33 static int g_last_xserver_error_code = 0; | |
| 34 | |
| 35 int XServerErrorHandler(Display* display, XErrorEvent* error_event) { | |
| 36 DCHECK(g_xserver_error_trap_enabled); | |
| 37 g_last_xserver_error_code = error_event->error_code; | |
| 38 return 0; | |
| 39 } | |
| 40 | |
| 41 void EnableXServerErrorTrap() { | |
| 42 DCHECK(!g_xserver_error_trap_enabled); | |
| 43 XSetErrorHandler(&XServerErrorHandler); | |
| 44 g_xserver_error_trap_enabled = true; | |
| 45 g_last_xserver_error_code = 0; | |
| 46 } | |
| 47 | |
| 48 int GetLastXServerError() { | |
| 49 DCHECK(g_xserver_error_trap_enabled); | |
| 50 XSetErrorHandler(NULL); | |
| 51 g_xserver_error_trap_enabled = false; | |
| 52 return g_last_xserver_error_code; | |
| 53 } | |
| 54 | |
| 55 #endif // !defined(TOOLKIT_GTK) | |
| 56 | |
| 57 } // namespace | |
| 58 | |
| 59 namespace remoting { | |
| 60 | |
| 61 XServerPixelBuffer::XServerPixelBuffer() | |
| 62 : display_(NULL), root_window_(0), | |
| 63 root_window_size_(SkISize::Make(0, 0)), x_image_(NULL), | |
| 64 shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) { | |
| 65 } | |
| 66 | |
| 67 XServerPixelBuffer::~XServerPixelBuffer() { | |
| 68 Release(); | |
| 69 } | |
| 70 | |
| 71 void XServerPixelBuffer::Release() { | |
| 72 if (x_image_) { | |
| 73 XDestroyImage(x_image_); | |
| 74 x_image_ = NULL; | |
| 75 } | |
| 76 if (shm_pixmap_) { | |
| 77 XFreePixmap(display_, shm_pixmap_); | |
| 78 shm_pixmap_ = 0; | |
| 79 } | |
| 80 if (shm_gc_) { | |
| 81 XFreeGC(display_, shm_gc_); | |
| 82 shm_gc_ = NULL; | |
| 83 } | |
| 84 if (shm_segment_info_) { | |
| 85 if (shm_segment_info_->shmaddr != reinterpret_cast<char*>(-1)) | |
| 86 shmdt(shm_segment_info_->shmaddr); | |
| 87 if (shm_segment_info_->shmid != -1) | |
| 88 shmctl(shm_segment_info_->shmid, IPC_RMID, 0); | |
| 89 delete shm_segment_info_; | |
| 90 shm_segment_info_ = NULL; | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 void XServerPixelBuffer::Init(Display* display, | |
| 95 const SkISize& screen_size) { | |
| 96 Release(); | |
| 97 display_ = display; | |
| 98 root_window_size_ = screen_size; | |
| 99 int default_screen = DefaultScreen(display_); | |
| 100 root_window_ = RootWindow(display_, default_screen); | |
| 101 InitShm(default_screen); | |
| 102 } | |
| 103 | |
| 104 // static | |
| 105 SkISize XServerPixelBuffer::GetRootWindowSize(Display* display) { | |
| 106 XWindowAttributes root_attr; | |
| 107 XGetWindowAttributes(display, DefaultRootWindow(display), &root_attr); | |
| 108 return SkISize::Make(root_attr.width, root_attr.height); | |
| 109 } | |
| 110 | |
| 111 void XServerPixelBuffer::InitShm(int screen) { | |
| 112 Visual* default_visual = DefaultVisual(display_, screen); | |
| 113 int default_depth = DefaultDepth(display_, screen); | |
| 114 | |
| 115 int major, minor; | |
| 116 Bool havePixmaps; | |
| 117 if (!XShmQueryVersion(display_, &major, &minor, &havePixmaps)) | |
| 118 // Shared memory not supported. CaptureRect will use the XImage API instead. | |
| 119 return; | |
| 120 | |
| 121 bool using_shm = false; | |
| 122 shm_segment_info_ = new XShmSegmentInfo; | |
| 123 shm_segment_info_->shmid = -1; | |
| 124 shm_segment_info_->shmaddr = reinterpret_cast<char*>(-1); | |
| 125 shm_segment_info_->readOnly = False; | |
| 126 x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap, | |
| 127 0, shm_segment_info_, root_window_size_.width(), | |
| 128 root_window_size_.height()); | |
| 129 if (x_image_) { | |
| 130 shm_segment_info_->shmid = shmget( | |
| 131 IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height, | |
| 132 IPC_CREAT | 0600); | |
| 133 if (shm_segment_info_->shmid != -1) { | |
| 134 shm_segment_info_->shmaddr = x_image_->data = | |
| 135 reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0)); | |
| 136 if (x_image_->data != reinterpret_cast<char*>(-1)) { | |
| 137 EnableXServerErrorTrap(); | |
| 138 using_shm = XShmAttach(display_, shm_segment_info_); | |
| 139 XSync(display_, False); | |
| 140 if (GetLastXServerError() != 0) | |
| 141 using_shm = false; | |
| 142 if (using_shm) { | |
| 143 VLOG(1) << "Using X shared memory segment " | |
| 144 << shm_segment_info_->shmid; | |
| 145 } | |
| 146 } | |
| 147 } else { | |
| 148 LOG(WARNING) << "Failed to get shared memory segment. " | |
| 149 "Performance may be degraded."; | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 if (!using_shm) { | |
| 154 LOG(WARNING) << "Not using shared memory. Performance may be degraded."; | |
| 155 Release(); | |
| 156 return; | |
| 157 } | |
| 158 | |
| 159 if (havePixmaps) | |
| 160 havePixmaps = InitPixmaps(default_depth); | |
| 161 | |
| 162 shmctl(shm_segment_info_->shmid, IPC_RMID, 0); | |
| 163 shm_segment_info_->shmid = -1; | |
| 164 | |
| 165 VLOG(1) << "Using X shared memory extension v" << major << "." << minor | |
| 166 << " with" << (havePixmaps?"":"out") << " pixmaps."; | |
| 167 } | |
| 168 | |
| 169 bool XServerPixelBuffer::InitPixmaps(int depth) { | |
| 170 if (XShmPixmapFormat(display_) != ZPixmap) | |
| 171 return false; | |
| 172 | |
| 173 EnableXServerErrorTrap(); | |
| 174 shm_pixmap_ = XShmCreatePixmap(display_, root_window_, | |
| 175 shm_segment_info_->shmaddr, | |
| 176 shm_segment_info_, | |
| 177 root_window_size_.width(), | |
| 178 root_window_size_.height(), depth); | |
| 179 XSync(display_, False); | |
| 180 if (GetLastXServerError() != 0) { | |
| 181 // |shm_pixmap_| is not not valid because the request was not processed | |
| 182 // by the X Server, so zero it. | |
| 183 shm_pixmap_ = 0; | |
| 184 return false; | |
| 185 } | |
| 186 | |
| 187 EnableXServerErrorTrap(); | |
| 188 XGCValues shm_gc_values; | |
| 189 shm_gc_values.subwindow_mode = IncludeInferiors; | |
| 190 shm_gc_values.graphics_exposures = False; | |
| 191 shm_gc_ = XCreateGC(display_, root_window_, | |
| 192 GCSubwindowMode | GCGraphicsExposures, | |
| 193 &shm_gc_values); | |
| 194 XSync(display_, False); | |
| 195 if (GetLastXServerError() != 0) { | |
| 196 XFreePixmap(display_, shm_pixmap_); | |
| 197 shm_pixmap_ = 0; | |
| 198 shm_gc_ = 0; // See shm_pixmap_ comment above. | |
| 199 return false; | |
| 200 } | |
| 201 | |
| 202 return true; | |
| 203 } | |
| 204 | |
| 205 void XServerPixelBuffer::Synchronize() { | |
| 206 if (shm_segment_info_ && !shm_pixmap_) { | |
| 207 // XShmGetImage can fail if the display is being reconfigured. | |
| 208 EnableXServerErrorTrap(); | |
| 209 XShmGetImage(display_, root_window_, x_image_, 0, 0, AllPlanes); | |
| 210 GetLastXServerError(); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 uint8* XServerPixelBuffer::CaptureRect(const SkIRect& rect) { | |
| 215 DCHECK(SkIRect::MakeSize(root_window_size_).contains(rect)); | |
| 216 if (shm_segment_info_) { | |
| 217 if (shm_pixmap_) { | |
| 218 XCopyArea(display_, root_window_, shm_pixmap_, shm_gc_, | |
| 219 rect.fLeft, rect.fTop, rect.width(), rect.height(), | |
| 220 rect.fLeft, rect.fTop); | |
| 221 XSync(display_, False); | |
| 222 } | |
| 223 return reinterpret_cast<uint8*>(x_image_->data) + | |
| 224 rect.fTop * x_image_->bytes_per_line + | |
| 225 rect.fLeft * x_image_->bits_per_pixel / 8; | |
| 226 } else { | |
| 227 if (x_image_) | |
| 228 XDestroyImage(x_image_); | |
| 229 x_image_ = XGetImage(display_, root_window_, rect.fLeft, rect.fTop, | |
| 230 rect.width(), rect.height(), AllPlanes, ZPixmap); | |
| 231 return reinterpret_cast<uint8*>(x_image_->data); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 int XServerPixelBuffer::GetStride() const { | |
| 236 return x_image_->bytes_per_line; | |
| 237 } | |
| 238 | |
| 239 int XServerPixelBuffer::GetDepth() const { | |
| 240 return x_image_->depth; | |
| 241 } | |
| 242 | |
| 243 int XServerPixelBuffer::GetBitsPerPixel() const { | |
| 244 return x_image_->bits_per_pixel; | |
| 245 } | |
| 246 | |
| 247 int XServerPixelBuffer::GetRedMask() const { | |
| 248 return x_image_->red_mask; | |
| 249 } | |
| 250 | |
| 251 int XServerPixelBuffer::GetBlueMask() const { | |
| 252 return x_image_->blue_mask; | |
| 253 } | |
| 254 | |
| 255 int XServerPixelBuffer::GetGreenMask() const { | |
| 256 return x_image_->green_mask; | |
| 257 } | |
| 258 | |
| 259 int XServerPixelBuffer::GetRedShift() const { | |
| 260 return ffs(x_image_->red_mask) - 1; | |
| 261 } | |
| 262 | |
| 263 int XServerPixelBuffer::GetBlueShift() const { | |
| 264 return ffs(x_image_->blue_mask) - 1; | |
| 265 } | |
| 266 | |
| 267 int XServerPixelBuffer::GetGreenShift() const { | |
| 268 return ffs(x_image_->green_mask) - 1; | |
| 269 } | |
| 270 | |
| 271 bool XServerPixelBuffer::IsRgb() const { | |
| 272 return GetRedShift() == 16 && GetGreenShift() == 8 && GetBlueShift() == 0; | |
| 273 } | |
| 274 | |
| 275 } // namespace remoting | |
| OLD | NEW |