Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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, ¤t_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 Loading... | |
| 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 |
| OLD | NEW |