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

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

Issue 10918224: Cross-platform plumbing for resize-to-client (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reviewer comments. Created 8 years, 3 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 | Annotate | Revision Log
OLDNEW
(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/desktop_resizer.h"
6
7 #include <string.h>
8 #include <X11/extensions/Xrandr.h>
9 #include <X11/Xlib.h>
10
11 #include "base/command_line.h"
12 #include "base/logging.h"
13
14 // On Linux, we use the xrandr extension to change the desktop size. This has
15 // a number of quirks that make the code more complex:
16 //
17 // 1. It's not possible to change the size of an existing mode. Instead, the
18 // mode must be deleted and recreated.
19 // 2. It's not possible to delete a mode that's in use.
20 // 3. Errors are communicated via Xlib's spectacularly unhelpful mechanism
21 // of terminating the process unless you install an error handler.
22 //
23 // The basic approach is to create a new mode with the correct size, switch to
24 // it, then delete the old one. Since the new mode must have a different name,
25 // and we want the current mode name to be consistent, we then recreate the old
26 // mode at the new size, switch to it and delete the temporary. The reason for
27 // name consistency is to allow a future CL to disable resize-to-client if the
28 // user has changed the mode to something other than "Chrome Remote Desktop
29 // client size". It doesn't make the code significantly more complex.
30
31 namespace remoting {
32
33 namespace {
Wez 2012/09/15 22:39:08 nit: blank line
34 // Temporarily install an alternative handler for X errors. The default handler
35 // exits the process, which is not what we want.
36 // TODO(jamiewalch): This doesn't really belong here, and should provide some
37 // means of checking whether or not anything went wrong.
38 class ScopedXErrorHandler {
39 public:
40 typedef int (*Handler)(Display*, XErrorEvent*);
41 ScopedXErrorHandler(Handler handler) {
42 previous_handler_ = XSetErrorHandler(handler);
43 }
44 ~ScopedXErrorHandler() {
45 XSetErrorHandler(previous_handler_);
46 }
47
48 static int Ignore(Display* display, XErrorEvent* event) {
49 LOG(INFO) << "Ignoring error.";
50 return 0;
51 }
52
53 private:
54 Handler previous_handler_;
55
56 DISALLOW_COPY_AND_ASSIGN(ScopedXErrorHandler);
57 };
58
59 class ScopedXGrabServer {
Wez 2012/09/15 22:39:08 nit: Add comment describing what this does?
60 public:
61 ScopedXGrabServer(Display* display)
62 : display_(display) {
63 XGrabServer(display_);
64 }
65 ~ScopedXGrabServer() {
66 XUngrabServer(display_);
67 XFlush(display_);
68 }
69 private:
70 Display* display_;
71
72 DISALLOW_COPY_AND_ASSIGN(ScopedXGrabServer);
73 };
74
75 // Wrapper class for the XRRScreenResources struct.
76 class ScreenResources {
77 public:
78 ScreenResources() : resources_(NULL) {
79 }
80
81 ~ScreenResources() {
82 Release();
83 }
84
85 bool Refresh(Display* display, Window window) {
86 Release();
87 resources_ = XRRGetScreenResources(display, window);
88 return resources_ != NULL;
89 }
90
91 void Release() {
92 if (resources_) {
93 XRRFreeScreenResources(resources_);
94 resources_ = NULL;
95 }
96 }
97
98 RRMode GetIdForMode(const char* name) {
99 CHECK(resources_);
100 for (int i = 0; i < resources_->nmode; ++i) {
101 const XRRModeInfo& mode = resources_->modes[i];
102 if (strcmp(mode.name, name) == 0) {
103 return mode.id;
104 }
105 }
106 return 0;
107 }
108
109 // For now, assume we're only ever interested in the first output.
Wez 2012/09/15 22:39:08 First output = first display?
110 RROutput GetOutput() {
Wez 2012/09/15 22:39:08 nit: GetFirstOutput?
111 CHECK(resources_);
112 return resources_->outputs[0];
113 }
114
115 // For now, assume we're only ever interested in the first crtc.
116 RRCrtc GetCrtc() {
117 CHECK(resources_);
118 return resources_->crtcs[0];
119 }
120
121 XRROutputInfo* GetOutputInfo(Display* display, RROutput output_id) {
122 CHECK(resources_);
123 return XRRGetOutputInfo(display, resources_, output_id);
124 }
125
126 XRRScreenResources* get() { return resources_; }
127
128 private:
129 XRRScreenResources* resources_;
130 };
131
132
133 class DesktopResizerLinux : public DesktopResizer {
134 public:
135 DesktopResizerLinux();
136 virtual ~DesktopResizerLinux();
137
138 // DesktopResizer interface
139 virtual SkISize GetSize() OVERRIDE;
140 virtual void SetSize(const SkISize& size) OVERRIDE;
141
142 private:
143 // Create a mode, and attach it to the primary output. If the mode already
144 // exists, it is left unchanged.
145 void CreateMode(const char* name, int width, int height);
146
147 // Remove the specified mode from the primary output, and delete it. If the
148 // mode is in use, it is not deleted.
149 void DeleteMode(const char* name);
150
151 // Switch the primary output to the specified mode. If name is NULL, the
152 // primary output is disabled instead, which is required in order before
Lambros 2012/09/14 23:21:22 nit: "in order to change" or "required before" or
Jamie 2012/09/15 00:28:26 Done.
153 // changing its size.
154 void SwitchToMode(const char* name);
155
156 Display* display_;
157 Window root_;
158 ScreenResources resources_;
159 bool exact_resize_;
160
161 DISALLOW_COPY_AND_ASSIGN(DesktopResizerLinux);
162 };
163
164 } // namespace
165
166 DesktopResizerLinux::DesktopResizerLinux()
167 : display_(XOpenDisplay(NULL)),
168 root_(RootWindow(display_, DefaultScreen(display_))),
169 exact_resize_(CommandLine::ForCurrentProcess()->
170 HasSwitch("server-supports-exact-resize")) {
171 }
172
173 DesktopResizerLinux::~DesktopResizerLinux() {
174 XCloseDisplay(display_);
175 }
176
177 SkISize DesktopResizerLinux::GetSize() {
178 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore);
Wez 2012/09/15 22:39:08 nit: blank line after this
179 SkISize result = SkISize::Make(0, 0);
180 int num_sizes;
181 XRRScreenSize *sizes = XRRSizes(display_, 0, &num_sizes);
182 // If xrandr is not supported, num_sizes is set to zero.
183 if (num_sizes) {
184 XRRScreenConfiguration *config = XRRGetScreenInfo(display_, root_);
185 Rotation rotation;
186 SizeID size_id = XRRConfigCurrentConfiguration(config, &rotation);
187 CHECK(size_id < num_sizes);
188 result = SkISize::Make(sizes[size_id].width, sizes[size_id].height);
189 }
190 return result;
191 }
192
193 void DesktopResizerLinux::SetSize(const SkISize& size) {
194 // TODO(jamiewalch): Resize to the closest available size if we can't
195 // support exact-size matching.
196 if (!exact_resize_) {
197 return;
198 }
199
200 // Ignore X errors encountered while resizing the display. We might hit an
201 // error, for example if xrandr has been used to add a mode with the same
202 // name as our temporary mode, or to remove the "client size" mode. We don't
203 // want to terminate the process if this happens.
204 ScopedXErrorHandler handler(ScopedXErrorHandler::Ignore);
205
206 // Grab the X server while we're changing the display size. This ensures
207 // that the display configuration doesn't change under our feet.
208 ScopedXGrabServer grabber(display_);
209
210 // Clamp the specified size to something valid for the X server.
211 int min_width = 0, min_height = 0, max_width = 0, max_height = 0;
212 XRRGetScreenSizeRange(display_, root_,
213 &min_width, &min_height,
214 &max_width, &max_height);
215 int width = std::min(std::max(size.width(), min_width), max_width);
216 int height = std::min(std::max(size.height(), min_height), max_height);
217 LOG(INFO) << "Changing desktop size to " << width << "x" << height;
218
219 // The name of the mode representing the current client view size and the
220 // temporary mode used for the reasons described at the top of this file.
221 // The former should be localized if it's user-visible; the latter only
222 // exists briefly and does not need to localized.
223 const char* kModeName = "Chrome Remote Desktop client size";
224 const char* kTempModeName = "Chrome Remote Desktop temporary mode";
Wez 2012/09/15 22:39:08 nit: Mode names without whitespace in them will be
225
226 // Actually do the resize operation, preserving the current mode name. Note
227 // that we have to detach the output from any mode in order to resize it
228 // (strictly speaking, this is only required when reducing the size, but it
229 // seems safe to do it regardless).
230 CreateMode(kTempModeName, width, height);
231 SwitchToMode(NULL);
232 XRRSetScreenSize(display_, root_, width, height, -1, -1);
233 SwitchToMode(kTempModeName);
234 DeleteMode(kModeName);
235 CreateMode(kModeName, width, height);
236 SwitchToMode(kModeName);
237 DeleteMode(kTempModeName);
238 }
239
240 void DesktopResizerLinux::CreateMode(const char* name, int width, int height) {
241 XRRModeInfo mode;
242 memset(&mode, 0, sizeof(mode));
243 mode.width = width;
244 mode.height = height;
245 mode.name = const_cast<char*>(name);
246 mode.nameLength = strlen(name);
247 XRRCreateMode(display_, root_, &mode);
248
249 if (!resources_.Refresh(display_, root_)) {
250 return;
251 }
252 RRMode mode_id = resources_.GetIdForMode(name);
253 if (!mode_id) {
254 return;
255 }
256 XRRAddOutputMode(display_, resources_.GetOutput(), mode_id);
257 }
258
259 void DesktopResizerLinux::DeleteMode(const char* name) {
260 RRMode mode_id = resources_.GetIdForMode(name);
261 if (mode_id) {
262 XRRDeleteOutputMode(display_, resources_.GetOutput(), mode_id);
263 XRRDestroyMode(display_, mode_id);
264 resources_.Refresh(display_, root_);
265 }
266 }
267
268 void DesktopResizerLinux::SwitchToMode(const char* name) {
269 RRMode mode_id = None;
270 RROutput* outputs = NULL;
271 int number_of_outputs = 0;
272 if (name) {
273 mode_id = resources_.GetIdForMode(name);
274 CHECK(mode_id);
275 outputs = resources_.get()->outputs;
276 number_of_outputs = resources_.get()->noutput;
277 }
278 XRRSetCrtcConfig(display_, resources_.get(), resources_.GetCrtc(),
279 CurrentTime, 0, 0, mode_id, 1, outputs, number_of_outputs);
280 }
281
282 scoped_ptr<DesktopResizer> DesktopResizer::Create() {
283 return scoped_ptr<DesktopResizer>(new DesktopResizerLinux);
284 }
285
286 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698