OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 // This file implements a X11 chromoting client. | 5 // This file implements a simple X11 chromoting client. |
6 | 6 |
7 #include <iostream> | 7 #include <iostream> |
8 | 8 |
9 #include "base/at_exit.h" | 9 #include "base/at_exit.h" |
10 #include "base/message_loop.h" | 10 #include "remoting/client/chromoting_client.h" |
11 #include "base/stl_util-inl.h" | 11 #include "remoting/client/client_config.h" |
12 #include "base/task.h" | |
13 #include "base/waitable_event.h" | |
14 #include "remoting/client/client_util.h" | 12 #include "remoting/client/client_util.h" |
15 #include "remoting/client/host_connection.h" | |
16 #include "remoting/client/jingle_host_connection.h" | 13 #include "remoting/client/jingle_host_connection.h" |
17 #include "remoting/jingle_glue/jingle_thread.h" | 14 #include "remoting/client/x11_view.h" |
| 15 #include "remoting/client/x11_input_handler.h" |
18 | 16 |
19 // Include Xlib at the end because it clashes with ClientMessage defined in | 17 void ClientQuit(MessageLoop* loop) { |
20 // the protocol buffer. | 18 loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); |
21 // x11_view.h also includes Xlib.h so put it behind all other headers but | 19 } |
22 // before Xlib.h | |
23 #include "remoting/client/x11_view.h" | |
24 #include <X11/Xlib.h> | |
25 | |
26 using remoting::JingleHostConnection; | |
27 using remoting::JingleThread; | |
28 | |
29 namespace remoting { | |
30 | |
31 class X11Client : public HostConnection::HostEventCallback { | |
32 public: | |
33 X11Client(MessageLoop* loop, base::WaitableEvent* client_done) | |
34 : message_loop_(loop), | |
35 client_done_(client_done), | |
36 display_(NULL), | |
37 window_(0), | |
38 width_(0), | |
39 height_(0) { | |
40 } | |
41 | |
42 virtual ~X11Client() { | |
43 DCHECK(!display_); | |
44 DCHECK(!window_); | |
45 } | |
46 | |
47 //////////////////////////////////////////////////////////////////////////// | |
48 // HostConnection::EventHandler implementations. | |
49 virtual void HandleMessages(HostConnection* conn, | |
50 remoting::HostMessageList* messages) { | |
51 for (size_t i = 0; i < messages->size(); ++i) { | |
52 HostMessage* msg = (*messages)[i]; | |
53 if (msg->has_init_client()) { | |
54 message_loop_->PostTask( | |
55 FROM_HERE, NewRunnableMethod(this, &X11Client::DoInitClient, msg)); | |
56 } else if (msg->has_begin_update_stream()) { | |
57 message_loop_->PostTask( | |
58 FROM_HERE, | |
59 NewRunnableMethod(this, &X11Client::DoBeginUpdate, msg)); | |
60 } else if (msg->has_update_stream_packet()) { | |
61 message_loop_->PostTask( | |
62 FROM_HERE, | |
63 NewRunnableMethod(this, &X11Client::DoHandleUpdate, msg)); | |
64 } else if (msg->has_end_update_stream()) { | |
65 message_loop_->PostTask( | |
66 FROM_HERE, NewRunnableMethod(this, &X11Client::DoEndUpdate, msg)); | |
67 } else { | |
68 NOTREACHED() << "Unknown message received"; | |
69 } | |
70 } | |
71 // Assume we have processed all the messages. | |
72 messages->clear(); | |
73 } | |
74 | |
75 virtual void OnConnectionOpened(HostConnection* conn) { | |
76 std::cout << "Connection established." << std::endl; | |
77 } | |
78 | |
79 virtual void OnConnectionClosed(HostConnection* conn) { | |
80 std::cout << "Connection closed." << std::endl; | |
81 client_done_->Signal(); | |
82 } | |
83 | |
84 virtual void OnConnectionFailed(HostConnection* conn) { | |
85 std::cout << "Conection failed." << std::endl; | |
86 client_done_->Signal(); | |
87 } | |
88 | |
89 void InitX11() { | |
90 message_loop_->PostTask(FROM_HERE, | |
91 NewRunnableMethod(this, &X11Client::DoInitX11)); | |
92 } | |
93 | |
94 void DoInitX11() { | |
95 display_ = XOpenDisplay(NULL); | |
96 if (!display_) { | |
97 std::cout << "Error - cannot open display" << std::endl; | |
98 client_done_->Signal(); | |
99 return; | |
100 } | |
101 | |
102 // Get properties of the screen. | |
103 int screen = DefaultScreen(display_); | |
104 int root_window = RootWindow(display_, screen); | |
105 | |
106 // Creates the window. | |
107 window_ = XCreateSimpleWindow(display_, root_window, 1, 1, 640, 480, 0, | |
108 BlackPixel(display_, screen), | |
109 BlackPixel(display_, screen)); | |
110 DCHECK(window_); | |
111 XStoreName(display_, window_, "X11 Remoting"); | |
112 | |
113 // Specifies what kind of messages we want to receive. | |
114 XSelectInput(display_, window_, ExposureMask | ButtonPressMask); | |
115 XMapWindow(display_, window_); | |
116 } | |
117 | |
118 void DestroyX11() { | |
119 message_loop_->PostTask(FROM_HERE, | |
120 NewRunnableMethod(this, &X11Client::DoDestroyX11)); | |
121 } | |
122 | |
123 void DoDestroyX11() { | |
124 if (display_ && window_) { | |
125 // Shutdown the window system. | |
126 XDestroyWindow(display_, window_); | |
127 XCloseDisplay(display_); | |
128 display_ = NULL; | |
129 window_ = 0; | |
130 } | |
131 } | |
132 | |
133 private: | |
134 // This method is executed on the main loop. | |
135 void DoInitClient(HostMessage* msg) { | |
136 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
137 DCHECK(msg->has_init_client()); | |
138 scoped_ptr<HostMessage> deleter(msg); | |
139 | |
140 // Saves the dimension and resize the window. | |
141 width_ = msg->init_client().width(); | |
142 height_ = msg->init_client().height(); | |
143 LOG(INFO) << "Init client received: " << width_ << "x" << height_; | |
144 XResizeWindow(display_, window_, width_, height_); | |
145 | |
146 // Now construct the X11View that renders the remote desktop. | |
147 view_.reset(new X11View(display_, window_, width_, height_)); | |
148 | |
149 // Schedule the event handler to process the event queue of X11. | |
150 ScheduleX11EventHandler(); | |
151 } | |
152 | |
153 // The following methods are executed on the same thread as libjingle. | |
154 void DoBeginUpdate(HostMessage* msg) { | |
155 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
156 DCHECK(msg->has_begin_update_stream()); | |
157 | |
158 view_->HandleBeginUpdateStream(msg); | |
159 } | |
160 | |
161 void DoHandleUpdate(HostMessage* msg) { | |
162 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
163 DCHECK(msg->has_update_stream_packet()); | |
164 | |
165 view_->HandleUpdateStreamPacket(msg); | |
166 } | |
167 | |
168 void DoEndUpdate(HostMessage* msg) { | |
169 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
170 DCHECK(msg->has_end_update_stream()); | |
171 | |
172 view_->HandleEndUpdateStream(msg); | |
173 } | |
174 | |
175 void DoProcessX11Events() { | |
176 DCHECK_EQ(message_loop_, MessageLoop::current()); | |
177 if (XPending(display_)) { | |
178 XEvent e; | |
179 XNextEvent(display_, &e); | |
180 if (e.type == Expose) { | |
181 // Tell the ChromotingView to paint again. | |
182 view_->Paint(); | |
183 } else if (e.type == ButtonPress) { | |
184 // TODO(hclam): Implement. | |
185 NOTIMPLEMENTED(); | |
186 } else { | |
187 // TODO(hclam): Implement. | |
188 NOTIMPLEMENTED(); | |
189 } | |
190 } | |
191 | |
192 // Schedule the next event handler. | |
193 ScheduleX11EventHandler(); | |
194 } | |
195 | |
196 void ScheduleX11EventHandler() { | |
197 // Schedule a delayed task to process X11 events in 10ms. | |
198 static const int kProcessEventsInterval = 10; | |
199 message_loop_->PostDelayedTask( | |
200 FROM_HERE, | |
201 NewRunnableMethod(this, &X11Client::DoProcessX11Events), | |
202 kProcessEventsInterval); | |
203 } | |
204 | |
205 MessageLoop* message_loop_; | |
206 base::WaitableEvent* client_done_; | |
207 | |
208 // Members used for display. | |
209 Display* display_; | |
210 Window window_; | |
211 | |
212 // Dimension of the window. They are initialized when InitClient message is | |
213 // received. | |
214 int width_; | |
215 int height_; | |
216 | |
217 scoped_ptr<ChromotingView> view_; | |
218 | |
219 DISALLOW_COPY_AND_ASSIGN(X11Client); | |
220 }; | |
221 | |
222 } // namespace remoting | |
223 | |
224 DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::X11Client); | |
225 | 20 |
226 int main(int argc, char** argv) { | 21 int main(int argc, char** argv) { |
227 base::AtExitManager at_exit; | 22 base::AtExitManager at_exit; |
228 std::string host_jid; | |
229 std::string username; | |
230 std::string auth_token; | |
231 | 23 |
232 if (!remoting::GetLoginInfo(argc, argv, &host_jid, &username, &auth_token)) { | 24 remoting::ClientConfig config; |
233 std::cout << "Cannot obtain login info" << std::endl; | 25 if (!remoting::GetLoginInfoFromArgs(argc, argv, &config)) { |
| 26 std::cout << "Unable to obtain login info" << std::endl; |
234 return 1; | 27 return 1; |
235 } | 28 } |
236 | 29 |
237 JingleThread network_thread; | 30 MessageLoop ui_loop; |
238 network_thread.Start(); | 31 remoting::ClientContext context; |
| 32 remoting::JingleHostConnection connection(&context); |
| 33 remoting::X11View view; |
| 34 remoting::X11InputHandler input_handler(&context, &view); |
| 35 remoting::ChromotingClient client(&config, &context, &connection, &view, |
| 36 &input_handler, NewRunnableFunction(&ClientQuit, &ui_loop)); |
239 | 37 |
240 base::WaitableEvent client_done(false, false); | 38 // Run the client on a new MessageLoop until |
241 remoting::X11Client client(network_thread.message_loop(), &client_done); | 39 context.Start(); |
242 JingleHostConnection connection(&network_thread); | 40 client.Start(); |
243 connection.Connect(username, auth_token, host_jid, &client); | 41 ui_loop.Run(); |
244 | 42 |
245 client.InitX11(); | 43 client.Stop(); |
246 | 44 context.Stop(); |
247 // Wait until the main loop is done. | |
248 client_done.Wait(); | |
249 | |
250 connection.Disconnect(); | |
251 | |
252 client.DestroyX11(); | |
253 | |
254 // Quit the current message loop. | |
255 network_thread.message_loop()->PostTask(FROM_HERE, | |
256 new MessageLoop::QuitTask()); | |
257 network_thread.Stop(); | |
258 | 45 |
259 return 0; | 46 return 0; |
260 } | 47 } |
OLD | NEW |