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

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

Powered by Google App Engine
This is Rietveld 408576698