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

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 further comments Created 4 years, 6 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 a
19 // the client resolution exactly. To support best-resolution matching, it would 19 // patched 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 bool has_randr_;
157 164
158 DISALLOW_COPY_AND_ASSIGN(DesktopResizerX11); 165 DISALLOW_COPY_AND_ASSIGN(DesktopResizerX11);
159 }; 166 };
160 167
161 DesktopResizerX11::DesktopResizerX11() 168 DesktopResizerX11::DesktopResizerX11()
162 : display_(XOpenDisplay(nullptr)), 169 : display_(XOpenDisplay(nullptr)),
163 screen_(DefaultScreen(display_)), 170 screen_(DefaultScreen(display_)),
164 root_(RootWindow(display_, screen_)), 171 root_(RootWindow(display_, screen_)),
165 exact_resize_(base::CommandLine::ForCurrentProcess()-> 172 exact_resize_(base::CommandLine::ForCurrentProcess()->
166 HasSwitch("server-supports-exact-resize")) { 173 HasSwitch("server-supports-exact-resize")) {
174 int rr_event_base;
175 int rr_error_base;
176
177 has_randr_ = XRRQueryExtension(display_, &rr_event_base, &rr_error_base);
178
167 XRRSelectInput(display_, root_, RRScreenChangeNotifyMask); 179 XRRSelectInput(display_, root_, RRScreenChangeNotifyMask);
168 } 180 }
169 181
170 DesktopResizerX11::~DesktopResizerX11() { 182 DesktopResizerX11::~DesktopResizerX11() {
171 XCloseDisplay(display_); 183 XCloseDisplay(display_);
172 } 184 }
173 185
174 ScreenResolution DesktopResizerX11::GetCurrentResolution() { 186 ScreenResolution DesktopResizerX11::GetCurrentResolution() {
175 if (!exact_resize_) { 187 // Xrandr requires that we process RRScreenChangeNotify events, otherwise
176 // TODO(jamiewalch): Remove this early return if we decide to support 188 // DisplayWidth and DisplayHeight do not return the current values. Normally,
177 // non-Xvfb servers. 189 // this would be done via a central X event loop, but we don't have one, hence
178 return ScreenResolution(); 190 // this horrible hack.
179 }
180
181 // TODO(lambroslambrou): Xrandr requires that we process RRScreenChangeNotify
182 // 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
184 // don't have one, hence this horrible hack.
185 // 191 //
186 // Note that the WatchFileDescriptor approach taken in XServerClipboard 192 // Note that the WatchFileDescriptor approach taken in XServerClipboard
187 // doesn't work here because resize events have already been read from the 193 // 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 194 // X server socket by the time the resize function returns, hence the
189 // file descriptor is never seen as readable. 195 // file descriptor is never seen as readable.
190 while (XEventsQueued(display_, QueuedAlready)) { 196 if (has_randr_) {
191 XEvent event; 197 while (XEventsQueued(display_, QueuedAlready)) {
192 XNextEvent(display_, &event); 198 XEvent event;
193 XRRUpdateConfiguration(&event); 199 XNextEvent(display_, &event);
200 XRRUpdateConfiguration(&event);
201 }
194 } 202 }
195 203
196 ScreenResolution result( 204 ScreenResolution result(
197 webrtc::DesktopSize( 205 webrtc::DesktopSize(
198 DisplayWidth(display_, DefaultScreen(display_)), 206 DisplayWidth(display_, screen_),
199 DisplayHeight(display_, DefaultScreen(display_))), 207 DisplayHeight(display_, screen_)),
200 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); 208 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
201 return result; 209 return result;
202 } 210 }
203 211
204 std::list<ScreenResolution> DesktopResizerX11::GetSupportedResolutions( 212 std::list<ScreenResolution> DesktopResizerX11::GetSupportedResolutions(
205 const ScreenResolution& preferred) { 213 const ScreenResolution& preferred) {
206 std::list<ScreenResolution> result; 214 std::list<ScreenResolution> result;
207 if (exact_resize_) { 215 if (exact_resize_) {
208 // Clamp the specified size to something valid for the X server. 216 // 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; 217 int min_width = 0, min_height = 0, max_width = 0, max_height = 0;
210 XRRGetScreenSizeRange(display_, root_, 218 XRRGetScreenSizeRange(display_, root_,
211 &min_width, &min_height, 219 &min_width, &min_height,
212 &max_width, &max_height); 220 &max_width, &max_height);
213 int width = std::min(std::max(preferred.dimensions().width(), min_width), 221 int width = std::min(std::max(preferred.dimensions().width(), min_width),
214 max_width); 222 max_width);
215 int height = std::min(std::max(preferred.dimensions().height(), min_height), 223 int height = std::min(std::max(preferred.dimensions().height(), min_height),
216 max_height); 224 max_height);
217 // Additionally impose a minimum size of 640x480, since anything smaller 225 // Additionally impose a minimum size of 640x480, since anything smaller
218 // doesn't seem very useful. 226 // doesn't seem very useful.
219 ScreenResolution actual( 227 ScreenResolution actual(
220 webrtc::DesktopSize(std::max(640, width), std::max(480, height)), 228 webrtc::DesktopSize(std::max(640, width), std::max(480, height)),
221 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); 229 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
222 result.push_back(actual); 230 result.push_back(actual);
223 } else { 231 } else if (has_randr_) {
224 // TODO(jamiewalch): Return the list of supported resolutions if we can't 232 // Retrieve supported resolutions with RandR
225 // support exact-size matching. 233 XRRScreenConfiguration *config = XRRGetScreenInfo(display_, root_);
234 if (config) {
235 int num_sizes = 0;
236 XRRScreenSize *sizes = XRRConfigSizes(config, &num_sizes);
237
238 for (int i = 0; i < num_sizes; ++i) {
239 result.push_back(ScreenResolution(
240 webrtc::DesktopSize(sizes[i].width, sizes[i].height),
241 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)));
242 }
243
244 XRRFreeScreenConfigInfo(config);
245 }
226 } 246 }
227 return result; 247 return result;
228 } 248 }
229 249
230 void DesktopResizerX11::SetResolution(const ScreenResolution& resolution) { 250 void DesktopResizerX11::SetResolution(const ScreenResolution& resolution) {
231 if (!exact_resize_) { 251 if (!has_randr_) {
232 // TODO(jamiewalch): Remove this early return if we decide to support
233 // non-Xvfb servers.
234 return; 252 return;
235 } 253 }
236 254
237 // Ignore X errors encountered while resizing the display. We might hit an 255 // 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 256 // 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 257 // name as our temporary mode, or to remove the "client resolution" mode. We
240 // don't want to terminate the process if this happens. 258 // don't want to terminate the process if this happens.
241 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore()); 259 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore());
242 260
243 // Grab the X server while we're changing the display resolution. This ensures 261 // Grab the X server while we're changing the display resolution. This ensures
244 // that the display configuration doesn't change under our feet. 262 // that the display configuration doesn't change under our feet.
245 ScopedXGrabServer grabber(display_); 263 ScopedXGrabServer grabber(display_);
246 264
265 if (exact_resize_) {
266 SetResolutionNewMode(resolution);
267 } else {
268 SetResolutionExistingMode(resolution);
269 }
270 }
271
272 void DesktopResizerX11::RestoreResolution(const ScreenResolution& original) {
273 SetResolution(original);
274 }
275
276 void DesktopResizerX11::SetResolutionNewMode(
277 const ScreenResolution& resolution) {
247 // The name of the mode representing the current client view resolution and 278 // 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. 279 // 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 280 // The former should be localized if it's user-visible; the latter only
250 // exists briefly and does not need to localized. 281 // exists briefly and does not need to localized.
251 const char* kModeName = "Chrome Remote Desktop client resolution"; 282 const char* kModeName = "Chrome Remote Desktop client resolution";
252 const char* kTempModeName = "Chrome Remote Desktop temporary mode"; 283 const char* kTempModeName = "Chrome Remote Desktop temporary mode";
253 284
254 // Actually do the resize operation, preserving the current mode name. Note 285 // 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 286 // 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 287 // (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(), 300 XRRSetScreenSize(display_, root_, resolution.dimensions().width(),
270 resolution.dimensions().height(), width_mm, height_mm); 301 resolution.dimensions().height(), width_mm, height_mm);
271 SwitchToMode(kTempModeName); 302 SwitchToMode(kTempModeName);
272 DeleteMode(kModeName); 303 DeleteMode(kModeName);
273 CreateMode(kModeName, resolution.dimensions().width(), 304 CreateMode(kModeName, resolution.dimensions().width(),
274 resolution.dimensions().height()); 305 resolution.dimensions().height());
275 SwitchToMode(kModeName); 306 SwitchToMode(kModeName);
276 DeleteMode(kTempModeName); 307 DeleteMode(kTempModeName);
277 } 308 }
278 309
279 void DesktopResizerX11::RestoreResolution(const ScreenResolution& original) { 310 void DesktopResizerX11::SetResolutionExistingMode(
280 // Since the desktop is only visible via a remote connection, the original 311 const ScreenResolution& resolution) {
281 // resolution of the desktop will never been seen and there's no point 312 XRRScreenConfiguration *config = XRRGetScreenInfo(display_, root_);
282 // restoring it; if we did, we'd just risk messing up the user's window 313 if (config) {
283 // layout. 314 int num_sizes = 0;
315 XRRScreenSize *sizes = XRRConfigSizes(config, &num_sizes);
316 Rotation current_rotation = 0;
317 XRRConfigCurrentConfiguration(config, &current_rotation);
318
319 for (int i = 0; i < num_sizes; ++i) {
320 if (sizes[i].width == resolution.dimensions().width()
321 && sizes[i].height == resolution.dimensions().height()) {
322 XRRSetScreenConfig(display_, config, root_, i, current_rotation,
323 CurrentTime);
324 break;
325 }
326 }
327
328 XRRFreeScreenConfigInfo(config);
329 }
284 } 330 }
285 331
286 void DesktopResizerX11::CreateMode(const char* name, int width, int height) { 332 void DesktopResizerX11::CreateMode(const char* name, int width, int height) {
287 XRRModeInfo mode; 333 XRRModeInfo mode;
288 memset(&mode, 0, sizeof(mode)); 334 memset(&mode, 0, sizeof(mode));
289 mode.width = width; 335 mode.width = width;
290 mode.height = height; 336 mode.height = height;
291 mode.name = const_cast<char*>(name); 337 mode.name = const_cast<char*>(name);
292 mode.nameLength = strlen(name); 338 mode.nameLength = strlen(name);
293 XRRCreateMode(display_, root_, &mode); 339 XRRCreateMode(display_, root_, &mode);
(...skipping 29 matching lines...) Expand all
323 } 369 }
324 XRRSetCrtcConfig(display_, resources_.get(), resources_.GetCrtc(), 370 XRRSetCrtcConfig(display_, resources_.get(), resources_.GetCrtc(),
325 CurrentTime, 0, 0, mode_id, 1, outputs, number_of_outputs); 371 CurrentTime, 0, 0, mode_id, 1, outputs, number_of_outputs);
326 } 372 }
327 373
328 std::unique_ptr<DesktopResizer> DesktopResizer::Create() { 374 std::unique_ptr<DesktopResizer> DesktopResizer::Create() {
329 return base::WrapUnique(new DesktopResizerX11); 375 return base::WrapUnique(new DesktopResizerX11);
330 } 376 }
331 377
332 } // namespace remoting 378 } // 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