| 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 |