| 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/host/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), x_image_(NULL), | |
| 63 shm_segment_info_(NULL), shm_pixmap_(0), shm_gc_(NULL) { | |
| 64 } | |
| 65 | |
| 66 XServerPixelBuffer::~XServerPixelBuffer() { | |
| 67 Release(); | |
| 68 } | |
| 69 | |
| 70 void XServerPixelBuffer::Release() { | |
| 71 if (x_image_) { | |
| 72 XDestroyImage(x_image_); | |
| 73 x_image_ = NULL; | |
| 74 } | |
| 75 if (shm_pixmap_) { | |
| 76 XFreePixmap(display_, shm_pixmap_); | |
| 77 shm_pixmap_ = 0; | |
| 78 } | |
| 79 if (shm_gc_) { | |
| 80 XFreeGC(display_, shm_gc_); | |
| 81 shm_gc_ = NULL; | |
| 82 } | |
| 83 if (shm_segment_info_) { | |
| 84 if (shm_segment_info_->shmaddr != reinterpret_cast<char*>(-1)) | |
| 85 shmdt(shm_segment_info_->shmaddr); | |
| 86 if (shm_segment_info_->shmid != -1) | |
| 87 shmctl(shm_segment_info_->shmid, IPC_RMID, 0); | |
| 88 delete shm_segment_info_; | |
| 89 shm_segment_info_ = NULL; | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 void XServerPixelBuffer::Init(Display* display) { | |
| 94 Release(); | |
| 95 display_ = display; | |
| 96 int default_screen = DefaultScreen(display_); | |
| 97 root_window_ = RootWindow(display_, default_screen); | |
| 98 InitShm(default_screen); | |
| 99 } | |
| 100 | |
| 101 void XServerPixelBuffer::InitShm(int screen) { | |
| 102 XWindowAttributes root_attr; | |
| 103 XGetWindowAttributes(display_, root_window_, &root_attr); | |
| 104 int width = root_attr.width, height = root_attr.height; | |
| 105 Visual* default_visual = DefaultVisual(display_, screen); | |
| 106 int default_depth = DefaultDepth(display_, screen); | |
| 107 | |
| 108 int major, minor; | |
| 109 Bool havePixmaps; | |
| 110 if (!XShmQueryVersion(display_, &major, &minor, &havePixmaps)) | |
| 111 // Shared memory not supported. CaptureRect will use the XImage API instead. | |
| 112 return; | |
| 113 | |
| 114 bool using_shm = false; | |
| 115 shm_segment_info_ = new XShmSegmentInfo; | |
| 116 shm_segment_info_->shmid = -1; | |
| 117 shm_segment_info_->shmaddr = reinterpret_cast<char*>(-1); | |
| 118 shm_segment_info_->readOnly = False; | |
| 119 x_image_ = XShmCreateImage(display_, default_visual, default_depth, ZPixmap, | |
| 120 0, shm_segment_info_, width, height); | |
| 121 if (x_image_) { | |
| 122 shm_segment_info_->shmid = shmget( | |
| 123 IPC_PRIVATE, x_image_->bytes_per_line * x_image_->height, | |
| 124 IPC_CREAT | 0600); | |
| 125 if (shm_segment_info_->shmid != -1) { | |
| 126 shm_segment_info_->shmaddr = x_image_->data = | |
| 127 reinterpret_cast<char*>(shmat(shm_segment_info_->shmid, 0, 0)); | |
| 128 if (x_image_->data != reinterpret_cast<char*>(-1)) { | |
| 129 EnableXServerErrorTrap(); | |
| 130 using_shm = XShmAttach(display_, shm_segment_info_); | |
| 131 XSync(display_, False); | |
| 132 if (GetLastXServerError() != 0) | |
| 133 using_shm = false; | |
| 134 if (using_shm) { | |
| 135 VLOG(1) << "Using X shared memory segment " | |
| 136 << shm_segment_info_->shmid; | |
| 137 } | |
| 138 } | |
| 139 } else { | |
| 140 LOG(WARNING) << "Failed to get shared memory segment. " | |
| 141 "Performance may be degraded."; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 if (!using_shm) { | |
| 146 LOG(WARNING) << "Not using shared memory. Performance may be degraded."; | |
| 147 Release(); | |
| 148 return; | |
| 149 } | |
| 150 | |
| 151 if (havePixmaps) | |
| 152 havePixmaps = InitPixmaps(width, height, default_depth); | |
| 153 | |
| 154 shmctl(shm_segment_info_->shmid, IPC_RMID, 0); | |
| 155 shm_segment_info_->shmid = -1; | |
| 156 | |
| 157 VLOG(1) << "Using X shared memory extension v" << major << "." << minor | |
| 158 << " with" << (havePixmaps?"":"out") << " pixmaps."; | |
| 159 } | |
| 160 | |
| 161 bool XServerPixelBuffer::InitPixmaps(int width, int height, int depth) { | |
| 162 if (XShmPixmapFormat(display_) != ZPixmap) | |
| 163 return false; | |
| 164 | |
| 165 EnableXServerErrorTrap(); | |
| 166 shm_pixmap_ = XShmCreatePixmap(display_, root_window_, | |
| 167 shm_segment_info_->shmaddr, | |
| 168 shm_segment_info_, | |
| 169 width, height, depth); | |
| 170 XSync(display_, False); | |
| 171 if (GetLastXServerError() != 0) { | |
| 172 // |shm_pixmap_| is not not valid because the request was not processed | |
| 173 // by the X Server, so zero it. | |
| 174 shm_pixmap_ = 0; | |
| 175 return false; | |
| 176 } | |
| 177 | |
| 178 EnableXServerErrorTrap(); | |
| 179 XGCValues shm_gc_values; | |
| 180 shm_gc_values.subwindow_mode = IncludeInferiors; | |
| 181 shm_gc_values.graphics_exposures = False; | |
| 182 shm_gc_ = XCreateGC(display_, root_window_, | |
| 183 GCSubwindowMode | GCGraphicsExposures, | |
| 184 &shm_gc_values); | |
| 185 XSync(display_, False); | |
| 186 if (GetLastXServerError() != 0) { | |
| 187 XFreePixmap(display_, shm_pixmap_); | |
| 188 shm_pixmap_ = 0; | |
| 189 shm_gc_ = 0; // See shm_pixmap_ comment above. | |
| 190 return false; | |
| 191 } | |
| 192 | |
| 193 return true; | |
| 194 } | |
| 195 | |
| 196 void XServerPixelBuffer::Synchronize() { | |
| 197 if (shm_segment_info_ && !shm_pixmap_) { | |
| 198 // XShmGetImage can fail if the display is being reconfigured. | |
| 199 EnableXServerErrorTrap(); | |
| 200 XShmGetImage(display_, root_window_, x_image_, 0, 0, AllPlanes); | |
| 201 GetLastXServerError(); | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 uint8* XServerPixelBuffer::CaptureRect(const SkIRect& rect) { | |
| 206 if (shm_segment_info_) { | |
| 207 if (shm_pixmap_) { | |
| 208 XCopyArea(display_, root_window_, shm_pixmap_, shm_gc_, | |
| 209 rect.fLeft, rect.fTop, rect.width(), rect.height(), | |
| 210 rect.fLeft, rect.fTop); | |
| 211 XSync(display_, False); | |
| 212 } | |
| 213 return reinterpret_cast<uint8*>(x_image_->data) + | |
| 214 rect.fTop * x_image_->bytes_per_line + | |
| 215 rect.fLeft * x_image_->bits_per_pixel / 8; | |
| 216 } else { | |
| 217 if (x_image_) | |
| 218 XDestroyImage(x_image_); | |
| 219 x_image_ = XGetImage(display_, root_window_, rect.fLeft, rect.fTop, | |
| 220 rect.width(), rect.height(), AllPlanes, ZPixmap); | |
| 221 return reinterpret_cast<uint8*>(x_image_->data); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 int XServerPixelBuffer::GetStride() const { | |
| 226 return x_image_->bytes_per_line; | |
| 227 } | |
| 228 | |
| 229 int XServerPixelBuffer::GetDepth() const { | |
| 230 return x_image_->depth; | |
| 231 } | |
| 232 | |
| 233 int XServerPixelBuffer::GetBitsPerPixel() const { | |
| 234 return x_image_->bits_per_pixel; | |
| 235 } | |
| 236 | |
| 237 int XServerPixelBuffer::GetRedMask() const { | |
| 238 return x_image_->red_mask; | |
| 239 } | |
| 240 | |
| 241 int XServerPixelBuffer::GetBlueMask() const { | |
| 242 return x_image_->blue_mask; | |
| 243 } | |
| 244 | |
| 245 int XServerPixelBuffer::GetGreenMask() const { | |
| 246 return x_image_->green_mask; | |
| 247 } | |
| 248 | |
| 249 int XServerPixelBuffer::GetRedShift() const { | |
| 250 return ffs(x_image_->red_mask) - 1; | |
| 251 } | |
| 252 | |
| 253 int XServerPixelBuffer::GetBlueShift() const { | |
| 254 return ffs(x_image_->blue_mask) - 1; | |
| 255 } | |
| 256 | |
| 257 int XServerPixelBuffer::GetGreenShift() const { | |
| 258 return ffs(x_image_->green_mask) - 1; | |
| 259 } | |
| 260 | |
| 261 bool XServerPixelBuffer::IsRgb() const { | |
| 262 return GetRedShift() == 16 && GetGreenShift() == 8 && GetBlueShift() == 0; | |
| 263 } | |
| 264 | |
| 265 } // namespace remoting | |
| OLD | NEW |