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

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: 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. For
18 // now, we only support resize-to-client for Xvfb-based servers that can match 18 // now, we only support resize-to-client for Xvfb-based servers that can match
Lambros 2016/05/21 00:57:00 Update this comment.
rkjnsn 2016/05/24 00:52:34 Done.
19 // the client resolution exactly. To support best-resolution matching, it would 19 // the client resolution exactly. To support best-resolution matching, it would
20 // be necessary to implement |GetSupportedResolutions|, but it's not considered 20 // be necessary to implement |GetSupportedResolutions|, but it's not considered
21 // a priority now. 21 // a priority now.
22 // 22 //
23 // Xrandr has a number of restrictions that make this code more complex: 23 // Xrandr has a number of restrictions that make this code more complex:
24 // 24 //
25 // 1. It's not possible to change the resolution of an existing mode. Instead, 25 // 1. It's not possible to change the resolution of an existing mode. Instead,
26 // the mode must be deleted and recreated. 26 // the mode must be deleted and recreated.
27 // 2. It's not possible to delete a mode that's in use. 27 // 2. It's not possible to delete a mode that's in use.
28 // 3. Errors are communicated via Xlib's spectacularly unhelpful mechanism 28 // 3. Errors are communicated via Xlib's spectacularly unhelpful mechanism
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 ~DesktopResizerX11() override; 129 ~DesktopResizerX11() override;
130 130
131 // DesktopResizer interface 131 // DesktopResizer interface
132 ScreenResolution GetCurrentResolution() override; 132 ScreenResolution GetCurrentResolution() override;
133 std::list<ScreenResolution> GetSupportedResolutions( 133 std::list<ScreenResolution> GetSupportedResolutions(
134 const ScreenResolution& preferred) override; 134 const ScreenResolution& preferred) override;
135 void SetResolution(const ScreenResolution& resolution) override; 135 void SetResolution(const ScreenResolution& resolution) override;
136 void RestoreResolution(const ScreenResolution& original) override; 136 void RestoreResolution(const ScreenResolution& original) override;
137 137
138 private: 138 private:
139 // Add a mode matching the specified resolution and switch to it.
140 void SetResolutionExact(const ScreenResolution& resolution);
141
142 // Attempt to switch to an existing mode matching the specified resolution
143 // using RandR, if such a resolution exists. Otherwise, do nothing.
144 void SetResolutionRandr(const ScreenResolution& resolution);
rkjnsn 2016/05/20 22:08:40 Are these decent names for these functions?
Jamie 2016/05/20 23:56:19 Since they both use RANDR, maybe SetResolutionNewM
rkjnsn 2016/05/24 00:52:34 Done.
145
139 // Create a mode, and attach it to the primary output. If the mode already 146 // Create a mode, and attach it to the primary output. If the mode already
140 // exists, it is left unchanged. 147 // exists, it is left unchanged.
141 void CreateMode(const char* name, int width, int height); 148 void CreateMode(const char* name, int width, int height);
142 149
143 // Remove the specified mode from the primary output, and delete it. If the 150 // Remove the specified mode from the primary output, and delete it. If the
144 // mode is in use, it is not deleted. 151 // mode is in use, it is not deleted.
145 void DeleteMode(const char* name); 152 void DeleteMode(const char* name);
146 153
147 // Switch the primary output to the specified mode. If name is nullptr, the 154 // Switch the primary output to the specified mode. If name is nullptr, the
148 // primary output is disabled instead, which is required before changing 155 // primary output is disabled instead, which is required before changing
149 // its resolution. 156 // its resolution.
150 void SwitchToMode(const char* name); 157 void SwitchToMode(const char* name);
151 158
152 Display* display_; 159 Display* display_;
153 int screen_; 160 int screen_;
154 Window root_; 161 Window root_;
155 ScreenResources resources_; 162 ScreenResources resources_;
156 bool exact_resize_; 163 bool exact_resize_;
164 int rr_event_base_;
165 int rr_error_base_;
rkjnsn 2016/05/20 22:08:39 We don't currently use either of these, so it migh
Jamie 2016/05/20 23:56:19 SGTM.
rkjnsn 2016/05/24 00:52:34 Done.
166 bool has_randr_;
157 167
158 DISALLOW_COPY_AND_ASSIGN(DesktopResizerX11); 168 DISALLOW_COPY_AND_ASSIGN(DesktopResizerX11);
159 }; 169 };
160 170
161 DesktopResizerX11::DesktopResizerX11() 171 DesktopResizerX11::DesktopResizerX11()
162 : display_(XOpenDisplay(nullptr)), 172 : display_(XOpenDisplay(nullptr)),
163 screen_(DefaultScreen(display_)), 173 screen_(DefaultScreen(display_)),
164 root_(RootWindow(display_, screen_)), 174 root_(RootWindow(display_, screen_)),
165 exact_resize_(base::CommandLine::ForCurrentProcess()-> 175 exact_resize_(base::CommandLine::ForCurrentProcess()->
166 HasSwitch("server-supports-exact-resize")) { 176 HasSwitch("server-supports-exact-resize")),
177 // TODO(rkjnsn): Do we need to handle the case where the display failed to
178 // open? It doesn't look like we do, elsewhere.
rkjnsn 2016/05/20 22:08:40 It appears we generally assume that XOpenDisplay s
Jamie 2016/05/20 23:56:19 If this is standard elsewhere, then it's okay to d
rkjnsn 2016/05/24 00:52:34 Done.
179 has_randr_(XRRQueryExtension(display_, &rr_event_base_,
180 &rr_error_base_)) {
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_) { 189 // TODO(rkjnsn): Don't DisplayWidth and DisplayHeight work even without RandR?
176 // TODO(jamiewalch): Remove this early return if we decide to support 190 // Would it make sense to return the size here, even when RandR isn't present?
177 // non-Xvfb servers. 191 // (Obviously we'd want to skip the XRRUpdateConfiguration loop.)
rkjnsn 2016/05/20 22:08:39 I think we could return something meaningful here
Jamie 2016/05/20 23:56:19 I'll defer to Lambros here, given his TODO
Lambros 2016/05/21 00:57:00 I can't remember why we return an invalid resoluti
rkjnsn 2016/05/24 00:52:34 Done.
192 if (!has_randr_) {
178 return ScreenResolution(); 193 return ScreenResolution();
179 } 194 }
180 195
196 // TODO(rkjnsn): What's the todo, here? To investigate another approach? To
197 // revisit if we ever get a central X event loop?
181 // TODO(lambroslambrou): Xrandr requires that we process RRScreenChangeNotify 198 // TODO(lambroslambrou): Xrandr requires that we process RRScreenChangeNotify
182 // events, otherwise DisplayWidth and DisplayHeight do not return the current 199 // 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 200 // values. Normally, this would be done via a central X event loop, but we
184 // don't have one, hence this horrible hack. 201 // don't have one, hence this horrible hack.
rkjnsn 2016/05/20 22:08:40 I wasn't sure what the actual TODO item is, here,
Lambros 2016/05/21 00:57:00 I don't know what the TODO is, either :) The probl
Sergey Ulanov 2016/05/22 20:00:17 I don't think there are any other X connections us
rkjnsn 2016/05/24 00:52:34 Are there any specific action items that I should
185 // 202 //
186 // Note that the WatchFileDescriptor approach taken in XServerClipboard 203 // Note that the WatchFileDescriptor approach taken in XServerClipboard
187 // doesn't work here because resize events have already been read from the 204 // 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 205 // X server socket by the time the resize function returns, hence the
189 // file descriptor is never seen as readable. 206 // file descriptor is never seen as readable.
190 while (XEventsQueued(display_, QueuedAlready)) { 207 while (XEventsQueued(display_, QueuedAlready)) {
191 XEvent event; 208 XEvent event;
192 XNextEvent(display_, &event); 209 XNextEvent(display_, &event);
193 XRRUpdateConfiguration(&event); 210 XRRUpdateConfiguration(&event);
194 } 211 }
195 212
196 ScreenResolution result( 213 ScreenResolution result(
197 webrtc::DesktopSize( 214 webrtc::DesktopSize(
198 DisplayWidth(display_, DefaultScreen(display_)), 215 DisplayWidth(display_, DefaultScreen(display_)),
199 DisplayHeight(display_, DefaultScreen(display_))), 216 DisplayHeight(display_, DefaultScreen(display_))),
217 // TODO(rkjnsn): Do we want to calculate this from the screen dimensions?
rkjnsn 2016/05/20 22:08:39 We seem to assume 96 DPI everywhere. Would it make
Jamie 2016/05/20 23:56:19 I wouldn't worry about it for now, unless it's a r
Sergey Ulanov 2016/05/22 20:00:17 This code is used only with Me2Me where we only su
rkjnsn 2016/05/24 00:52:34 Acknowledged.
200 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); 218 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
201 return result; 219 return result;
202 } 220 }
203 221
204 std::list<ScreenResolution> DesktopResizerX11::GetSupportedResolutions( 222 std::list<ScreenResolution> DesktopResizerX11::GetSupportedResolutions(
205 const ScreenResolution& preferred) { 223 const ScreenResolution& preferred) {
206 std::list<ScreenResolution> result; 224 std::list<ScreenResolution> result;
207 if (exact_resize_) { 225 if (exact_resize_) {
208 // Clamp the specified size to something valid for the X server. 226 // 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; 227 int min_width = 0, min_height = 0, max_width = 0, max_height = 0;
210 XRRGetScreenSizeRange(display_, root_, 228 XRRGetScreenSizeRange(display_, root_,
211 &min_width, &min_height, 229 &min_width, &min_height,
212 &max_width, &max_height); 230 &max_width, &max_height);
213 int width = std::min(std::max(preferred.dimensions().width(), min_width), 231 int width = std::min(std::max(preferred.dimensions().width(), min_width),
214 max_width); 232 max_width);
215 int height = std::min(std::max(preferred.dimensions().height(), min_height), 233 int height = std::min(std::max(preferred.dimensions().height(), min_height),
216 max_height); 234 max_height);
217 // Additionally impose a minimum size of 640x480, since anything smaller 235 // Additionally impose a minimum size of 640x480, since anything smaller
Lambros 2016/05/21 00:57:00 Maybe add a blank line before this comment, though
218 // doesn't seem very useful. 236 // doesn't seem very useful.
219 ScreenResolution actual( 237 ScreenResolution actual(
220 webrtc::DesktopSize(std::max(640, width), std::max(480, height)), 238 webrtc::DesktopSize(std::max(640, width), std::max(480, height)),
221 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); 239 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
222 result.push_back(actual); 240 result.push_back(actual);
223 } else { 241 } else if (has_randr_) {
224 // TODO(jamiewalch): Return the list of supported resolutions if we can't 242 // Retrieve supported resolutions with RandR
225 // support exact-size matching. 243 XRRScreenConfiguration *config = XRRGetScreenInfo(display_, root_);
244 if (config) {
245 int num_sizes = 0;
246 XRRScreenSize *sizes = XRRConfigSizes(config, &num_sizes);
247
248 for (int i = 0; i < num_sizes; ++i) {
249 result.push_back(ScreenResolution(
250 webrtc::DesktopSize(sizes[i].width, sizes[i].height),
251 // TODO(rkjnsn): Do we want to calculate this from the screen
Lambros 2016/05/21 00:57:01 nit: Blank line before comment.
252 // dimensions?
253 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)));
254 }
255
256 XRRFreeScreenConfigInfo(config);
Jamie 2016/05/20 23:56:19 Use the ScreenResources class to manage the lifeti
Lambros 2016/05/21 00:57:00 I guess you'd need a separate scoper, with a dtor
rkjnsn 2016/05/24 00:52:34 I wouldn't be able to reuse ScreenResources for th
257 }
226 } 258 }
227 return result; 259 return result;
228 } 260 }
229 261
230 void DesktopResizerX11::SetResolution(const ScreenResolution& resolution) { 262 void DesktopResizerX11::SetResolution(const ScreenResolution& resolution) {
231 if (!exact_resize_) { 263 if (!has_randr_) {
232 // TODO(jamiewalch): Remove this early return if we decide to support
233 // non-Xvfb servers.
234 return; 264 return;
235 } 265 }
236 266
237 // Ignore X errors encountered while resizing the display. We might hit an 267 // 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 268 // 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 269 // name as our temporary mode, or to remove the "client resolution" mode. We
240 // don't want to terminate the process if this happens. 270 // don't want to terminate the process if this happens.
241 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore()); 271 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore());
242 272
243 // Grab the X server while we're changing the display resolution. This ensures 273 // Grab the X server while we're changing the display resolution. This ensures
244 // that the display configuration doesn't change under our feet. 274 // that the display configuration doesn't change under our feet.
245 ScopedXGrabServer grabber(display_); 275 ScopedXGrabServer grabber(display_);
246 276
277 if (exact_resize_) {
278 SetResolutionExact(resolution);
279 } else {
280 SetResolutionRandr(resolution);
281 }
282 }
283
284 void DesktopResizerX11::RestoreResolution(const ScreenResolution& original) {
285 // Since the desktop is only visible via a remote connection, the original
286 // resolution of the desktop will never been seen and there's no point
287 // restoring it; if we did, we'd just risk messing up the user's window
288 // layout.
289
290 // TODO(rkjnsn): Previously we only changed the resolution while in curtain
291 // mode with exact resize support Since we now pick the best resolution
292 // wherever RandR is supported, presumably we want to actually do the restore
293 // if and only if RandR is supported and we are not in curtain mode.
Jamie 2016/05/20 23:56:19 Agreed; that makes sense.
rkjnsn 2016/05/24 00:52:34 Per our discussion, this now unconditionally calls
294 }
295
296 void DesktopResizerX11::SetResolutionExact(const ScreenResolution& resolution) {
247 // The name of the mode representing the current client view resolution and 297 // 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. 298 // 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 299 // The former should be localized if it's user-visible; the latter only
250 // exists briefly and does not need to localized. 300 // exists briefly and does not need to localized.
251 const char* kModeName = "Chrome Remote Desktop client resolution"; 301 const char* kModeName = "Chrome Remote Desktop client resolution";
252 const char* kTempModeName = "Chrome Remote Desktop temporary mode"; 302 const char* kTempModeName = "Chrome Remote Desktop temporary mode";
253 303
254 // Actually do the resize operation, preserving the current mode name. Note 304 // 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 305 // 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 306 // (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(), 319 XRRSetScreenSize(display_, root_, resolution.dimensions().width(),
270 resolution.dimensions().height(), width_mm, height_mm); 320 resolution.dimensions().height(), width_mm, height_mm);
271 SwitchToMode(kTempModeName); 321 SwitchToMode(kTempModeName);
272 DeleteMode(kModeName); 322 DeleteMode(kModeName);
273 CreateMode(kModeName, resolution.dimensions().width(), 323 CreateMode(kModeName, resolution.dimensions().width(),
274 resolution.dimensions().height()); 324 resolution.dimensions().height());
275 SwitchToMode(kModeName); 325 SwitchToMode(kModeName);
276 DeleteMode(kTempModeName); 326 DeleteMode(kTempModeName);
277 } 327 }
278 328
279 void DesktopResizerX11::RestoreResolution(const ScreenResolution& original) { 329 void DesktopResizerX11::SetResolutionRandr(const ScreenResolution& resolution) {
280 // Since the desktop is only visible via a remote connection, the original 330 XRRScreenConfiguration *config = XRRGetScreenInfo(display_, root_);
281 // resolution of the desktop will never been seen and there's no point 331 if (config) {
282 // restoring it; if we did, we'd just risk messing up the user's window 332 int num_sizes = 0;
283 // layout. 333 XRRScreenSize *sizes = XRRConfigSizes(config, &num_sizes);
334 Rotation current_rotation = 0;
335 XRRConfigCurrentConfiguration(config, &current_rotation);
336
337 for (int i = 0; i < num_sizes; ++i) {
338 if (sizes[i].width == resolution.dimensions().width()
339 && sizes[i].height == resolution.dimensions().height()) {
340 XRRSetScreenConfig(display_, config, root_, i, current_rotation,
341 CurrentTime);
342 break;
343 }
344 }
345
346 XRRFreeScreenConfigInfo(config);
Lambros 2016/05/21 00:57:00 If you do a custom scoper for XRRFreeScreenConfigI
347 }
284 } 348 }
285 349
286 void DesktopResizerX11::CreateMode(const char* name, int width, int height) { 350 void DesktopResizerX11::CreateMode(const char* name, int width, int height) {
287 XRRModeInfo mode; 351 XRRModeInfo mode;
288 memset(&mode, 0, sizeof(mode)); 352 memset(&mode, 0, sizeof(mode));
289 mode.width = width; 353 mode.width = width;
290 mode.height = height; 354 mode.height = height;
291 mode.name = const_cast<char*>(name); 355 mode.name = const_cast<char*>(name);
292 mode.nameLength = strlen(name); 356 mode.nameLength = strlen(name);
293 XRRCreateMode(display_, root_, &mode); 357 XRRCreateMode(display_, root_, &mode);
(...skipping 29 matching lines...) Expand all
323 } 387 }
324 XRRSetCrtcConfig(display_, resources_.get(), resources_.GetCrtc(), 388 XRRSetCrtcConfig(display_, resources_.get(), resources_.GetCrtc(),
325 CurrentTime, 0, 0, mode_id, 1, outputs, number_of_outputs); 389 CurrentTime, 0, 0, mode_id, 1, outputs, number_of_outputs);
326 } 390 }
327 391
328 std::unique_ptr<DesktopResizer> DesktopResizer::Create() { 392 std::unique_ptr<DesktopResizer> DesktopResizer::Create() {
329 return base::WrapUnique(new DesktopResizerX11); 393 return base::WrapUnique(new DesktopResizerX11);
330 } 394 }
331 395
332 } // namespace remoting 396 } // 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