Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: remoting/host/desktop_resizer_x11.cc

Issue 2004613002: Add support for inexact resize on X11 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address first round of comments Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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/host/desktop_resizer.h" 5 #include "remoting/host/desktop_resizer.h"
6 6
7 #include <X11/Xlib.h> 7 #include <X11/Xlib.h>
8 #include <X11/extensions/Xrandr.h> 8 #include <X11/extensions/Xrandr.h>
9 #include <string.h> 9 #include <string.h>
10 10
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/macros.h" 12 #include "base/macros.h"
13 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
14 #include "remoting/base/logging.h" 14 #include "remoting/base/logging.h"
15 #include "remoting/host/linux/x11_util.h" 15 #include "remoting/host/linux/x11_util.h"
16 16
17 // On Linux, we use the xrandr extension to change the desktop resolution. For 17 // On Linux, we use the xrandr extension to change the desktop resolution. In
18 // now, we only support resize-to-client for Xvfb-based servers that can match 18 // curtain mode, we do exact resize where supported (currently only using our
Lambros 2016/06/08 00:12:39 "our" is a bit weird. It's not obvious (or desirab
rkjnsn 2016/06/08 01:32:42 I'll change it to read "(currently only using a pa
19 // the client resolution exactly. To support best-resolution matching, it would 19 // custom Xvfb server). Otherwise, we try to pick the best resolution from the
20 // be necessary to implement |GetSupportedResolutions|, but it's not considered 20 // existing modes.
21 // a priority now.
22 // 21 //
23 // Xrandr has a number of restrictions that make this code more complex: 22 // Xrandr has a number of restrictions that make exact resize more complex:
24 // 23 //
25 // 1. It's not possible to change the resolution of an existing mode. Instead, 24 // 1. It's not possible to change the resolution of an existing mode. Instead,
26 // the mode must be deleted and recreated. 25 // the mode must be deleted and recreated.
27 // 2. It's not possible to delete a mode that's in use. 26 // 2. It's not possible to delete a mode that's in use.
28 // 3. Errors are communicated via Xlib's spectacularly unhelpful mechanism 27 // 3. Errors are communicated via Xlib's spectacularly unhelpful mechanism
29 // of terminating the process unless you install an error handler. 28 // of terminating the process unless you install an error handler.
30 // 29 //
31 // The basic approach is as follows: 30 // The basic approach is as follows:
32 // 31 //
33 // 1. Create a new mode with the correct resolution; 32 // 1. Create a new mode with the correct resolution;
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 ~DesktopResizerX11() override; 128 ~DesktopResizerX11() override;
130 129
131 // DesktopResizer interface 130 // DesktopResizer interface
132 ScreenResolution GetCurrentResolution() override; 131 ScreenResolution GetCurrentResolution() override;
133 std::list<ScreenResolution> GetSupportedResolutions( 132 std::list<ScreenResolution> GetSupportedResolutions(
134 const ScreenResolution& preferred) override; 133 const ScreenResolution& preferred) override;
135 void SetResolution(const ScreenResolution& resolution) override; 134 void SetResolution(const ScreenResolution& resolution) override;
136 void RestoreResolution(const ScreenResolution& original) override; 135 void RestoreResolution(const ScreenResolution& original) override;
137 136
138 private: 137 private:
138 // Add a mode matching the specified resolution and switch to it.
139 void SetResolutionNewMode(const ScreenResolution& resolution);
140
141 // Attempt to switch to an existing mode matching the specified resolution
142 // using RandR, if such a resolution exists. Otherwise, do nothing.
143 void SetResolutionExistingMode(const ScreenResolution& resolution);
144
139 // Create a mode, and attach it to the primary output. If the mode already 145 // Create a mode, and attach it to the primary output. If the mode already
140 // exists, it is left unchanged. 146 // exists, it is left unchanged.
141 void CreateMode(const char* name, int width, int height); 147 void CreateMode(const char* name, int width, int height);
142 148
143 // Remove the specified mode from the primary output, and delete it. If the 149 // Remove the specified mode from the primary output, and delete it. If the
144 // mode is in use, it is not deleted. 150 // mode is in use, it is not deleted.
145 void DeleteMode(const char* name); 151 void DeleteMode(const char* name);
146 152
147 // Switch the primary output to the specified mode. If name is nullptr, the 153 // Switch the primary output to the specified mode. If name is nullptr, the
148 // primary output is disabled instead, which is required before changing 154 // primary output is disabled instead, which is required before changing
149 // its resolution. 155 // its resolution.
150 void SwitchToMode(const char* name); 156 void SwitchToMode(const char* name);
151 157
152 Display* display_; 158 Display* display_;
153 int screen_; 159 int screen_;
154 Window root_; 160 Window root_;
155 ScreenResources resources_; 161 ScreenResources resources_;
156 bool exact_resize_; 162 bool exact_resize_;
163 int rr_event_base_;
164 int rr_error_base_;
165 bool has_randr_;
157 166
158 DISALLOW_COPY_AND_ASSIGN(DesktopResizerX11); 167 DISALLOW_COPY_AND_ASSIGN(DesktopResizerX11);
159 }; 168 };
160 169
161 DesktopResizerX11::DesktopResizerX11() 170 DesktopResizerX11::DesktopResizerX11()
162 : display_(XOpenDisplay(nullptr)), 171 : display_(XOpenDisplay(nullptr)),
163 screen_(DefaultScreen(display_)), 172 screen_(DefaultScreen(display_)),
164 root_(RootWindow(display_, screen_)), 173 root_(RootWindow(display_, screen_)),
165 exact_resize_(base::CommandLine::ForCurrentProcess()-> 174 exact_resize_(base::CommandLine::ForCurrentProcess()->
166 HasSwitch("server-supports-exact-resize")) { 175 HasSwitch("server-supports-exact-resize")) {
176 int rr_event_base;
Lambros 2016/06/08 00:12:39 Don't need these, since you already have rr_event_
rkjnsn 2016/06/08 01:32:42 Oops. The goal was to avoid adding the rr_event_ba
177 int rr_error_base;
178
179 has_randr_ = XRRQueryExtension(display_, &rr_event_base, &rr_error_base);
180
167 XRRSelectInput(display_, root_, RRScreenChangeNotifyMask); 181 XRRSelectInput(display_, root_, RRScreenChangeNotifyMask);
168 } 182 }
169 183
170 DesktopResizerX11::~DesktopResizerX11() { 184 DesktopResizerX11::~DesktopResizerX11() {
171 XCloseDisplay(display_); 185 XCloseDisplay(display_);
172 } 186 }
173 187
174 ScreenResolution DesktopResizerX11::GetCurrentResolution() { 188 ScreenResolution DesktopResizerX11::GetCurrentResolution() {
175 if (!exact_resize_) {
176 // TODO(jamiewalch): Remove this early return if we decide to support
177 // non-Xvfb servers.
178 return ScreenResolution();
179 }
180
181 // TODO(lambroslambrou): Xrandr requires that we process RRScreenChangeNotify 189 // TODO(lambroslambrou): Xrandr requires that we process RRScreenChangeNotify
182 // events, otherwise DisplayWidth and DisplayHeight do not return the current 190 // events, otherwise DisplayWidth and DisplayHeight do not return the current
183 // values. Normally, this would be done via a central X event loop, but we 191 // values. Normally, this would be done via a central X event loop, but we
184 // don't have one, hence this horrible hack. 192 // don't have one, hence this horrible hack.
185 // 193 //
186 // Note that the WatchFileDescriptor approach taken in XServerClipboard 194 // Note that the WatchFileDescriptor approach taken in XServerClipboard
187 // doesn't work here because resize events have already been read from the 195 // doesn't work here because resize events have already been read from the
188 // X server socket by the time the resize function returns, hence the 196 // X server socket by the time the resize function returns, hence the
189 // file descriptor is never seen as readable. 197 // file descriptor is never seen as readable.
rkjnsn 2016/06/07 23:02:10 Given the previous discussion, are there any speci
Jamie 2016/06/07 23:40:11 I think just removing the TODO part of the comment
190 while (XEventsQueued(display_, QueuedAlready)) { 198 if (has_randr_) {
191 XEvent event; 199 while (XEventsQueued(display_, QueuedAlready)) {
192 XNextEvent(display_, &event); 200 XEvent event;
193 XRRUpdateConfiguration(&event); 201 XNextEvent(display_, &event);
202 XRRUpdateConfiguration(&event);
203 }
194 } 204 }
195 205
196 ScreenResolution result( 206 ScreenResolution result(
197 webrtc::DesktopSize( 207 webrtc::DesktopSize(
198 DisplayWidth(display_, DefaultScreen(display_)), 208 DisplayWidth(display_, screen_),
199 DisplayHeight(display_, DefaultScreen(display_))), 209 DisplayHeight(display_, screen_)),
200 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); 210 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
201 return result; 211 return result;
202 } 212 }
203 213
204 std::list<ScreenResolution> DesktopResizerX11::GetSupportedResolutions( 214 std::list<ScreenResolution> DesktopResizerX11::GetSupportedResolutions(
205 const ScreenResolution& preferred) { 215 const ScreenResolution& preferred) {
206 std::list<ScreenResolution> result; 216 std::list<ScreenResolution> result;
207 if (exact_resize_) { 217 if (exact_resize_) {
208 // Clamp the specified size to something valid for the X server. 218 // Clamp the specified size to something valid for the X server.
209 int min_width = 0, min_height = 0, max_width = 0, max_height = 0; 219 int min_width = 0, min_height = 0, max_width = 0, max_height = 0;
210 XRRGetScreenSizeRange(display_, root_, 220 XRRGetScreenSizeRange(display_, root_,
211 &min_width, &min_height, 221 &min_width, &min_height,
212 &max_width, &max_height); 222 &max_width, &max_height);
213 int width = std::min(std::max(preferred.dimensions().width(), min_width), 223 int width = std::min(std::max(preferred.dimensions().width(), min_width),
214 max_width); 224 max_width);
215 int height = std::min(std::max(preferred.dimensions().height(), min_height), 225 int height = std::min(std::max(preferred.dimensions().height(), min_height),
216 max_height); 226 max_height);
217 // Additionally impose a minimum size of 640x480, since anything smaller 227 // Additionally impose a minimum size of 640x480, since anything smaller
218 // doesn't seem very useful. 228 // doesn't seem very useful.
219 ScreenResolution actual( 229 ScreenResolution actual(
220 webrtc::DesktopSize(std::max(640, width), std::max(480, height)), 230 webrtc::DesktopSize(std::max(640, width), std::max(480, height)),
221 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); 231 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
222 result.push_back(actual); 232 result.push_back(actual);
223 } else { 233 } else if (has_randr_) {
224 // TODO(jamiewalch): Return the list of supported resolutions if we can't 234 // Retrieve supported resolutions with RandR
225 // support exact-size matching. 235 XRRScreenConfiguration *config = XRRGetScreenInfo(display_, root_);
236 if (config) {
237 int num_sizes = 0;
238 XRRScreenSize *sizes = XRRConfigSizes(config, &num_sizes);
239
240 for (int i = 0; i < num_sizes; ++i) {
241 result.push_back(ScreenResolution(
242 webrtc::DesktopSize(sizes[i].width, sizes[i].height),
243 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)));
244 }
245
246 XRRFreeScreenConfigInfo(config);
rkjnsn 2016/06/07 23:02:10 As I noted before, I wouldn't be able to reuse Scr
Jamie 2016/06/07 23:40:10 No, I think this is fine. I didn't spot that it wa
247 }
226 } 248 }
227 return result; 249 return result;
228 } 250 }
229 251
230 void DesktopResizerX11::SetResolution(const ScreenResolution& resolution) { 252 void DesktopResizerX11::SetResolution(const ScreenResolution& resolution) {
231 if (!exact_resize_) { 253 if (!has_randr_) {
232 // TODO(jamiewalch): Remove this early return if we decide to support
233 // non-Xvfb servers.
234 return; 254 return;
235 } 255 }
236 256
237 // Ignore X errors encountered while resizing the display. We might hit an 257 // Ignore X errors encountered while resizing the display. We might hit an
238 // error, for example if xrandr has been used to add a mode with the same 258 // error, for example if xrandr has been used to add a mode with the same
239 // name as our temporary mode, or to remove the "client resolution" mode. We 259 // name as our temporary mode, or to remove the "client resolution" mode. We
240 // don't want to terminate the process if this happens. 260 // don't want to terminate the process if this happens.
241 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore()); 261 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore());
242 262
243 // Grab the X server while we're changing the display resolution. This ensures 263 // Grab the X server while we're changing the display resolution. This ensures
244 // that the display configuration doesn't change under our feet. 264 // that the display configuration doesn't change under our feet.
245 ScopedXGrabServer grabber(display_); 265 ScopedXGrabServer grabber(display_);
246 266
267 if (exact_resize_) {
268 SetResolutionNewMode(resolution);
269 } else {
270 SetResolutionExistingMode(resolution);
271 }
272 }
273
274 void DesktopResizerX11::RestoreResolution(const ScreenResolution& original) {
275 SetResolution(original);
276 }
277
278 void DesktopResizerX11::SetResolutionNewMode(
279 const ScreenResolution& resolution) {
247 // The name of the mode representing the current client view resolution and 280 // The name of the mode representing the current client view resolution and
248 // the temporary mode used for the reasons described at the top of this file. 281 // the temporary mode used for the reasons described at the top of this file.
249 // The former should be localized if it's user-visible; the latter only 282 // The former should be localized if it's user-visible; the latter only
250 // exists briefly and does not need to localized. 283 // exists briefly and does not need to localized.
251 const char* kModeName = "Chrome Remote Desktop client resolution"; 284 const char* kModeName = "Chrome Remote Desktop client resolution";
252 const char* kTempModeName = "Chrome Remote Desktop temporary mode"; 285 const char* kTempModeName = "Chrome Remote Desktop temporary mode";
253 286
254 // Actually do the resize operation, preserving the current mode name. Note 287 // Actually do the resize operation, preserving the current mode name. Note
255 // that we have to detach the output from any mode in order to resize it 288 // that we have to detach the output from any mode in order to resize it
256 // (strictly speaking, this is only required when reducing the size, but it 289 // (strictly speaking, this is only required when reducing the size, but it
(...skipping 12 matching lines...) Expand all
269 XRRSetScreenSize(display_, root_, resolution.dimensions().width(), 302 XRRSetScreenSize(display_, root_, resolution.dimensions().width(),
270 resolution.dimensions().height(), width_mm, height_mm); 303 resolution.dimensions().height(), width_mm, height_mm);
271 SwitchToMode(kTempModeName); 304 SwitchToMode(kTempModeName);
272 DeleteMode(kModeName); 305 DeleteMode(kModeName);
273 CreateMode(kModeName, resolution.dimensions().width(), 306 CreateMode(kModeName, resolution.dimensions().width(),
274 resolution.dimensions().height()); 307 resolution.dimensions().height());
275 SwitchToMode(kModeName); 308 SwitchToMode(kModeName);
276 DeleteMode(kTempModeName); 309 DeleteMode(kTempModeName);
277 } 310 }
278 311
279 void DesktopResizerX11::RestoreResolution(const ScreenResolution& original) { 312 void DesktopResizerX11::SetResolutionExistingMode(
280 // Since the desktop is only visible via a remote connection, the original 313 const ScreenResolution& resolution) {
281 // resolution of the desktop will never been seen and there's no point 314 XRRScreenConfiguration *config = XRRGetScreenInfo(display_, root_);
282 // restoring it; if we did, we'd just risk messing up the user's window 315 if (config) {
283 // layout. 316 int num_sizes = 0;
317 XRRScreenSize *sizes = XRRConfigSizes(config, &num_sizes);
318 Rotation current_rotation = 0;
319 XRRConfigCurrentConfiguration(config, &current_rotation);
320
321 for (int i = 0; i < num_sizes; ++i) {
322 if (sizes[i].width == resolution.dimensions().width()
323 && sizes[i].height == resolution.dimensions().height()) {
324 XRRSetScreenConfig(display_, config, root_, i, current_rotation,
325 CurrentTime);
326 break;
327 }
328 }
329
330 XRRFreeScreenConfigInfo(config);
331 }
284 } 332 }
285 333
286 void DesktopResizerX11::CreateMode(const char* name, int width, int height) { 334 void DesktopResizerX11::CreateMode(const char* name, int width, int height) {
287 XRRModeInfo mode; 335 XRRModeInfo mode;
288 memset(&mode, 0, sizeof(mode)); 336 memset(&mode, 0, sizeof(mode));
289 mode.width = width; 337 mode.width = width;
290 mode.height = height; 338 mode.height = height;
291 mode.name = const_cast<char*>(name); 339 mode.name = const_cast<char*>(name);
292 mode.nameLength = strlen(name); 340 mode.nameLength = strlen(name);
293 XRRCreateMode(display_, root_, &mode); 341 XRRCreateMode(display_, root_, &mode);
(...skipping 29 matching lines...) Expand all
323 } 371 }
324 XRRSetCrtcConfig(display_, resources_.get(), resources_.GetCrtc(), 372 XRRSetCrtcConfig(display_, resources_.get(), resources_.GetCrtc(),
325 CurrentTime, 0, 0, mode_id, 1, outputs, number_of_outputs); 373 CurrentTime, 0, 0, mode_id, 1, outputs, number_of_outputs);
326 } 374 }
327 375
328 std::unique_ptr<DesktopResizer> DesktopResizer::Create() { 376 std::unique_ptr<DesktopResizer> DesktopResizer::Create() {
329 return base::WrapUnique(new DesktopResizerX11); 377 return base::WrapUnique(new DesktopResizerX11);
330 } 378 }
331 379
332 } // namespace remoting 380 } // namespace remoting
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698