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 |