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 |