diff options
author | garykac@google.com <garykac@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-20 00:34:57 +0000 |
---|---|---|
committer | garykac@google.com <garykac@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-20 00:34:57 +0000 |
commit | 4d37c874d917cc370e188cfdef05bb629388421a (patch) | |
tree | 08f68e3ef47406ae69209f489832fa8f1c76448e | |
parent | 0af395eb3fa7d33473b4239a7b637fc6a7ce0e98 (diff) | |
download | chromium_src-4d37c874d917cc370e188cfdef05bb629388421a.zip chromium_src-4d37c874d917cc370e188cfdef05bb629388421a.tar.gz chromium_src-4d37c874d917cc370e188cfdef05bb629388421a.tar.bz2 |
Refactor the client code for the X11 version.
Make ChromotingViews responsible for initializing themselves.
Move all x11-related code into X11View.
Create InputCapturer class manage client input capture.
BUG=none
TEST=ran Win host + X11 client
Review URL: http://codereview.chromium.org/2861047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52973 0039d316-1c4b-4281-b951-d872f2087c98
28 files changed, 710 insertions, 638 deletions
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc index 39f18b4..7b77ee7 100644 --- a/remoting/client/chromoting_client.cc +++ b/remoting/client/chromoting_client.cc @@ -6,7 +6,10 @@ #include "base/message_loop.h" #include "remoting/client/chromoting_view.h" +#include "remoting/client/client_config.h" +#include "remoting/client/client_context.h" #include "remoting/client/host_connection.h" +#include "remoting/client/input_handler.h" static const uint32 kCreatedColor = 0xffccccff; static const uint32 kDisconnectedColor = 0xff00ccff; @@ -14,53 +17,106 @@ static const uint32 kFailedColor = 0xffcc00ff; namespace remoting { -ChromotingClient::ChromotingClient(MessageLoop* message_loop, +ChromotingClient::ChromotingClient(ClientConfig* config, + ClientContext* context, HostConnection* connection, - ChromotingView* view) - : message_loop_(message_loop), - state_(CREATED), - host_connection_(connection), - view_(view) { + ChromotingView* view, + InputHandler* input_handler, + CancelableTask* client_done) + : config_(config), + context_(context), + connection_(connection), + view_(view), + input_handler_(input_handler), + client_done_(client_done), + state_(CREATED) { } ChromotingClient::~ChromotingClient() { } +void ChromotingClient::Start() { + if (message_loop() != MessageLoop::current()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::Start)); + return; + } + + connection_->Connect(config_, this); + + if (!view_->Initialize()) { + ClientDone(); + } +} + +void ChromotingClient::Stop() { + if (message_loop() != MessageLoop::current()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::Stop)); + return; + } + + connection_->Disconnect(); + + view_->TearDown(); + + // Quit the current message loop. + message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} + +void ChromotingClient::ClientDone() { + if (client_done_ != NULL) { + message_loop()->PostTask(FROM_HERE, client_done_); + } +} + void ChromotingClient::Repaint() { - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::DoRepaint)); + if (message_loop() != MessageLoop::current()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::Repaint)); + return; + } + + view_->Paint(); } void ChromotingClient::SetViewport(int x, int y, int width, int height) { - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::DoSetViewport, - x, y, width, height)); + if (message_loop() != MessageLoop::current()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::SetViewport, + x, y, width, height)); + return; + } + + view_->SetViewport(x, y, width, height); } void ChromotingClient::HandleMessages(HostConnection* conn, HostMessageList* messages) { + if (message_loop() != MessageLoop::current()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::HandleMessages, + conn, messages)); + return; + } + for (size_t i = 0; i < messages->size(); ++i) { HostMessage* msg = (*messages)[i]; // TODO(ajwong): Consider creating a macro similar to the IPC message // mappings. Also reconsider the lifetime of the message object. if (msg->has_init_client()) { - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::DoInitClient, msg)); + InitClient(msg); } else if (msg->has_begin_update_stream()) { - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::DoBeginUpdate, msg)); + BeginUpdate(msg); } else if (msg->has_update_stream_packet()) { - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::DoHandleUpdate, msg)); + HandleUpdate(msg); } else if (msg->has_end_update_stream()) { - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::DoEndUpdate, msg)); + EndUpdate(msg); } else { NOTREACHED() << "Unknown message received"; } @@ -85,19 +141,18 @@ void ChromotingClient::OnConnectionFailed(HostConnection* conn) { } MessageLoop* ChromotingClient::message_loop() { - return message_loop_; + return context_->jingle_thread()->message_loop(); } void ChromotingClient::SetState(State s) { // TODO(ajwong): We actually may want state to be a shared variable. Think // through later. - message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(this, &ChromotingClient::DoSetState, s)); -} - -void ChromotingClient::DoSetState(State s) { - DCHECK_EQ(message_loop(), MessageLoop::current()); + if (message_loop() != MessageLoop::current()) { + message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ChromotingClient::SetState, s)); + return; + } state_ = s; switch (state_) { @@ -118,49 +173,40 @@ void ChromotingClient::DoSetState(State s) { break; } - DoRepaint(); + Repaint(); } -void ChromotingClient::DoRepaint() { - DCHECK_EQ(message_loop(), MessageLoop::current()); - - view_->Paint(); -} - -void ChromotingClient::DoSetViewport(int x, int y, int width, int height) { - DCHECK_EQ(message_loop(), MessageLoop::current()); - - view_->SetViewport(x, y, width, height); -} - -void ChromotingClient::DoInitClient(HostMessage* msg) { +void ChromotingClient::InitClient(HostMessage* msg) { DCHECK_EQ(message_loop(), MessageLoop::current()); DCHECK(msg->has_init_client()); scoped_ptr<HostMessage> deleter(msg); - // Saves the dimension and resize the window. + // Resize the window. int width = msg->init_client().width(); int height = msg->init_client().height(); LOG(INFO) << "Init client received geometry: " << width << "x" << height; - view_->SetBackingStoreSize(width, height); + view_->SetHostScreenSize(width, height); + + // Schedule the input handler to process the event queue. + input_handler_->Initialize(); } -void ChromotingClient::DoBeginUpdate(HostMessage* msg) { +void ChromotingClient::BeginUpdate(HostMessage* msg) { DCHECK_EQ(message_loop(), MessageLoop::current()); DCHECK(msg->has_begin_update_stream()); view_->HandleBeginUpdateStream(msg); } -void ChromotingClient::DoHandleUpdate(HostMessage* msg) { +void ChromotingClient::HandleUpdate(HostMessage* msg) { DCHECK_EQ(message_loop(), MessageLoop::current()); DCHECK(msg->has_update_stream_packet()); view_->HandleUpdateStreamPacket(msg); } -void ChromotingClient::DoEndUpdate(HostMessage* msg) { +void ChromotingClient::EndUpdate(HostMessage* msg) { DCHECK_EQ(message_loop(), MessageLoop::current()); DCHECK(msg->has_end_update_stream()); diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h index 50cc0dc..331c805 100644 --- a/remoting/client/chromoting_client.h +++ b/remoting/client/chromoting_client.h @@ -15,14 +15,25 @@ class MessageLoop; namespace remoting { class ChromotingView; +class ClientConfig; +class ClientContext; +class InputHandler; class ChromotingClient : public HostConnection::HostEventCallback { public: - ChromotingClient(MessageLoop* message_loop, + // Objects passed in are not owned by this class. + ChromotingClient(ClientConfig* config, + ClientContext* context, HostConnection* connection, - ChromotingView* view); + ChromotingView* view, + InputHandler* input_handler, + CancelableTask* client_done); virtual ~ChromotingClient(); + void Start(); + void Stop(); + void ClientDone(); + // Signals that the associated view may need updating. virtual void Repaint(); @@ -53,25 +64,24 @@ class ChromotingClient : public HostConnection::HostEventCallback { // Convenience method for modifying the state on this object's message loop. void SetState(State s); - // TODO(ajwong): Do all of these methods need to run on the client's thread? - void DoSetState(State s); - void DoRepaint(); - void DoSetViewport(int x, int y, int width, int height); - // Handles for chromotocol messages. - void DoInitClient(HostMessage* msg); - void DoBeginUpdate(HostMessage* msg); - void DoHandleUpdate(HostMessage* msg); - void DoEndUpdate(HostMessage* msg); + void InitClient(HostMessage* msg); + void BeginUpdate(HostMessage* msg); + void HandleUpdate(HostMessage* msg); + void EndUpdate(HostMessage* msg); + + // The following are not owned by this class. + ClientConfig* config_; + ClientContext* context_; + HostConnection* connection_; + ChromotingView* view_; + InputHandler* input_handler_; - MessageLoop* message_loop_; + // If non-NULL, this is called when the client is done. + CancelableTask* client_done_; State state_; - // Connection to views and hosts. Not owned. - HostConnection* host_connection_; - ChromotingView* view_; - DISALLOW_COPY_AND_ASSIGN(ChromotingClient); }; diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h index 503a903..a50b407 100644 --- a/remoting/client/chromoting_view.h +++ b/remoting/client/chromoting_view.h @@ -18,6 +18,12 @@ class ChromotingView { public: virtual ~ChromotingView() {} + // Initialize the common structures for the view. + virtual bool Initialize() = 0; + + // Free up resources allocated by this view. + virtual void TearDown() = 0; + // Tells the ChromotingView to paint the current image on the screen. // TODO(hclam): Add rects as parameter if needed. virtual void Paint() = 0; @@ -34,12 +40,11 @@ class ChromotingView { // extends past the end of the backing store, it is filled with black. virtual void SetViewport(int x, int y, int width, int height) = 0; - // Resize the underlying image that is displayed. This should match the size - // of the output from the decoder. + // Resize the underlying image that contains the host screen buffer. + // This should match the size of the output from the decoder. // - // TODO(ajwong): We need a better name. Look at how Java represents this - // stuff? - virtual void SetBackingStoreSize(int width, int height) = 0; + // TODO(garykac): This handles only 1 screen. We need multi-screen support. + virtual void SetHostScreenSize(int width, int height) = 0; // Handle the BeginUpdateStream message. virtual void HandleBeginUpdateStream(HostMessage* msg) = 0; diff --git a/remoting/client/client_config.h b/remoting/client/client_config.h new file mode 100644 index 0000000..a02d9e8 --- /dev/null +++ b/remoting/client/client_config.h @@ -0,0 +1,36 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_CLIENT_CLIENT_CONFIG_H_ +#define REMOTING_CLIENT_CLIENT_CONFIG_H_ + +#include <string> + +#include "base/basictypes.h" + +namespace remoting { + +class ClientConfig { + public: + ClientConfig() { } + + const std::string host_jid() { return host_jid_; } + const std::string username() { return username_; } + const std::string auth_token() { return auth_token_; } + + void set_host_jid(std::string val) { host_jid_ = val; } + void set_username(std::string val) { username_ = val; } + void set_auth_token(std::string val) { auth_token_ = val; } + + private: + std::string host_jid_; + std::string username_; + std::string auth_token_; + + DISALLOW_COPY_AND_ASSIGN(ClientConfig); +}; + +} // namespace remoting + +#endif // REMOTING_CLIENT_CLIENT_CONFIG_H_ diff --git a/remoting/client/client_context.cc b/remoting/client/client_context.cc new file mode 100644 index 0000000..64b7cc1 --- /dev/null +++ b/remoting/client/client_context.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/client/client_context.h" + +#include <string> + +#include "base/thread.h" +#include "remoting/jingle_glue/jingle_thread.h" + +namespace remoting { + +ClientContext::ClientContext() + : main_thread_("ChromotingClientMainThread"), + decode_thread_("ChromotingClientDecodeThread") { +} + +ClientContext::~ClientContext() { +} + +void ClientContext::Start() { + // Start all the threads. + main_thread_.Start(); + decode_thread_.Start(); + jingle_thread_.Start(); +} + +void ClientContext::Stop() { + // Stop all the threads. + jingle_thread_.Stop(); + decode_thread_.Stop(); + main_thread_.Stop(); +} + +JingleThread* ClientContext::jingle_thread() { + return &jingle_thread_; +} + +MessageLoop* ClientContext::jingle_message_loop() { + return jingle_thread_.message_loop(); +} + +MessageLoop* ClientContext::main_message_loop() { + return main_thread_.message_loop(); +} + +MessageLoop* ClientContext::decode_message_loop() { + return decode_thread_.message_loop(); +} + +} // namespace remoting diff --git a/remoting/client/client_context.h b/remoting/client/client_context.h new file mode 100644 index 0000000..03273fb --- /dev/null +++ b/remoting/client/client_context.h @@ -0,0 +1,48 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_CLIENT_CLIENT_CONTEXT_H_ +#define REMOTING_CLIENT_CLIENT_CONTEXT_H_ + +#include <string> + +#include "base/thread.h" +#include "remoting/jingle_glue/jingle_thread.h" + +namespace remoting { + +// A class that manages threads and running context for the chromoting client +// process. +class ClientContext { + public: + ClientContext(); + virtual ~ClientContext(); + + void Start(); + void Stop(); + + JingleThread* jingle_thread(); + MessageLoop* jingle_message_loop(); + + MessageLoop* main_message_loop(); + MessageLoop* decode_message_loop(); + + private: + // A thread that handles Jingle network operations (used in + // JingleHostConnection). + JingleThread jingle_thread_; + + // A thread that handles capture rate control and sending data to the + // HostConnection. + base::Thread main_thread_; + + // A thread that handles all decode operations. + base::Thread decode_thread_; + + DISALLOW_COPY_AND_ASSIGN(ClientContext); +}; + +} // namespace remoting + +#endif // REMOTING_CLIENT_CLIENT_CONTEXT_H_ diff --git a/remoting/client/client_util.cc b/remoting/client/client_util.cc index ddaee42..9e53682 100644 --- a/remoting/client/client_util.cc +++ b/remoting/client/client_util.cc @@ -4,20 +4,26 @@ #include "remoting/client/client_util.h" -#include <iostream> +#include <string> +#include <vector> #include "base/logging.h" +#include "base/string_util.h" +#include "remoting/client/client_config.h" + +using std::string; +using std::vector; namespace remoting { // Get host JID from command line arguments, or stdin if not specified. -bool GetLoginInfo(int argc, char** argv, - std::string* host_jid, - std::string* username, - std::string* auth_token) { +bool GetLoginInfoFromArgs(int argc, char** argv, ClientConfig* config) { bool found_host_jid = false; bool found_jid = false; bool found_auth_token = false; + string host_jid; + string username; + string auth_token; for (int i = 1; i < argc; i++) { std::string arg = argv[i]; @@ -27,14 +33,14 @@ bool GetLoginInfo(int argc, char** argv, << std::endl; } else { found_host_jid = true; - *host_jid = argv[i]; + host_jid = argv[i]; } } else if (arg == "--jid") { if (++i >= argc) { LOG(WARNING) << "Expected JID to follow --jid option" << std::endl; } else { found_jid = true; - *username = argv[i]; + username = argv[i]; } } else if (arg == "--token") { if (++i >= argc) { @@ -42,7 +48,7 @@ bool GetLoginInfo(int argc, char** argv, << std::endl; } else { found_auth_token = true; - *auth_token = argv[i]; + auth_token = argv[i]; } } else { LOG(WARNING) << "Unrecognized option: " << arg << std::endl; @@ -50,44 +56,63 @@ bool GetLoginInfo(int argc, char** argv, } if (!found_host_jid) { - std::cout << "Host JID: "; - std::cin >> *host_jid; - std::cin.ignore(); // Consume the leftover '\n' + return false; } // Validate the chromoting host JID. - if (host_jid->find("/chromoting") == std::string::npos) { - std::cerr << "Error: Expected Host JID in format: <jid>/chromoting<id>" - << std::endl; + if (host_jid.find("/chromoting") == std::string::npos) { return false; } if (!found_jid) { - // Get username (JID). - // Extract default JID from host_jid. - std::string default_username; - size_t jid_end = host_jid->find('/'); - if (jid_end != std::string::npos) { - default_username = host_jid->substr(0, jid_end); - } - std::cout << "JID [" << default_username << "]: "; - getline(std::cin, *username); - if (username->length() == 0) { - username->swap(default_username); - } - if (username->length() == 0) { - std::cerr << "Error: Expected valid JID username" << std::endl; - return false; - } + return false; } if (!found_auth_token) { - // Get authentication token. - std::cout << "Auth token: "; - getline(std::cin, *auth_token); - std::cout << std::endl; + return false; + } + + config->set_host_jid(host_jid); + config->set_username(username); + config->set_auth_token(auth_token); + return true; +} + +// Get host JID from command line arguments, or stdin if not specified. +bool GetLoginInfoFromUrlParams(const std::string& url, ClientConfig* config) { + // TODO(ajwong): We should use GURL or something. Don't parse this by hand! + + // The Url should be of the form: + // + // chrome://remoting?user=<userid>&auth=<authtoken>&jid=<hostjid> + // + vector<string> parts; + SplitString(url, '&', &parts); + if (parts.size() != 3) { + return false; + } + + size_t pos = parts[0].rfind('='); + if (pos == string::npos && (pos + 1) != string::npos) { + return false; + } + std::string username = parts[0].substr(pos + 1); + + pos = parts[1].rfind('='); + if (pos == string::npos && (pos + 1) != string::npos) { + return false; + } + std::string auth_token = parts[1].substr(pos + 1); + + pos = parts[2].rfind('='); + if (pos == string::npos && (pos + 1) != string::npos) { + return false; } + std::string host_jid = parts[2].substr(pos + 1); + config->set_host_jid(host_jid); + config->set_username(username); + config->set_auth_token(auth_token); return true; } diff --git a/remoting/client/client_util.h b/remoting/client/client_util.h index 44f3435..9aa1474 100644 --- a/remoting/client/client_util.h +++ b/remoting/client/client_util.h @@ -9,12 +9,16 @@ namespace remoting { +class ClientConfig; + // Get the login info from the cmdline args (or request from the console if -// not present) and write values into |host_jid|, |username| and |auth_token|. +// not present) and write values into |config|. +// Return true if successful. +bool GetLoginInfoFromArgs(int argc, char** argv, ClientConfig* config); + +// Get the login info from the URL params and write values into |config|. // Return true if successful. -bool GetLoginInfo(int argc, char** argv, - std::string* host_jid, std::string* username, - std::string* auth_token); +bool GetLoginInfoFromUrlParams(const std::string& url, ClientConfig* config); } // namespace remoting diff --git a/remoting/client/client_util_unittest.cc b/remoting/client/client_util_unittest.cc new file mode 100644 index 0000000..d9f2e7c --- /dev/null +++ b/remoting/client/client_util_unittest.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "remoting/client/client_config.h" +#include "remoting/client/client_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace remoting { + +// TODO(ajwong): Once ChromotingPlugin stablizes a little more, come up with +// sane unittests. +class ClientUtilTest : public testing::Test { + protected: + virtual void SetUp() { + } +}; + +TEST_F(ClientUtilTest, GetLoginInfoFromUrlParams) { + const char url[] = "chromotocol://hostid?user=auser&auth=someauth&jid=ajid"; + std::string user_id; + std::string auth_token; + std::string host_jid; + ClientConfig config; + + ASSERT_TRUE( + GetLoginInfoFromUrlParams(url, &config)); + + EXPECT_EQ("auser", config.username()); + EXPECT_EQ("someauth", config.auth_token()); + EXPECT_EQ("ajid", config.host_jid()); +} + +} // namespace remoting diff --git a/remoting/client/host_connection.h b/remoting/client/host_connection.h index 0667cc9..c53bc73 100644 --- a/remoting/client/host_connection.h +++ b/remoting/client/host_connection.h @@ -11,6 +11,8 @@ namespace remoting { +class ClientConfig; + class HostConnection { public: class HostEventCallback { @@ -37,9 +39,7 @@ class HostConnection { virtual ~HostConnection() {} // TODO(ajwong): We need to generalize this API. - virtual void Connect(const std::string& username, - const std::string& auth_token, - const std::string& host_jid, + virtual void Connect(ClientConfig* config, HostEventCallback* event_callback) = 0; virtual void Disconnect() = 0; diff --git a/remoting/client/input_handler.h b/remoting/client/input_handler.h new file mode 100644 index 0000000..fad3718 --- /dev/null +++ b/remoting/client/input_handler.h @@ -0,0 +1,28 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_CLIENT_INPUT_HANDLER_H_ +#define REMOTING_CLIENT_INPUT_HANDLER_H_ + +#include "base/basictypes.h" +#include "base/task.h" + +namespace remoting { + +class InputHandler { + public: + InputHandler() {} + virtual ~InputHandler() {} + + virtual void Initialize() = 0; + + protected: + DISALLOW_COPY_AND_ASSIGN(InputHandler); +}; + +} // namespace remoting + +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::InputHandler); + +#endif // REMOTING_CLIENT_INPUT_HANDLER_H_ diff --git a/remoting/client/jingle_host_connection.cc b/remoting/client/jingle_host_connection.cc index 9bf547f..2e1b0a1 100644 --- a/remoting/client/jingle_host_connection.cc +++ b/remoting/client/jingle_host_connection.cc @@ -4,28 +4,26 @@ #include "base/message_loop.h" #include "remoting/base/constants.h" +#include "remoting/client/client_config.h" #include "remoting/client/jingle_host_connection.h" #include "remoting/jingle_glue/jingle_thread.h" namespace remoting { -JingleHostConnection::JingleHostConnection(JingleThread* network_thread) - : network_thread_(network_thread), +JingleHostConnection::JingleHostConnection(ClientContext* context) + : context_(context), event_callback_(NULL) { } JingleHostConnection::~JingleHostConnection() { } -void JingleHostConnection::Connect(const std::string& username, - const std::string& password, - const std::string& host_jid, +void JingleHostConnection::Connect(ClientConfig* config, HostEventCallback* event_callback) { message_loop()->PostTask( FROM_HERE, NewRunnableMethod(this, &JingleHostConnection::DoConnect, - username, password, host_jid, - event_callback)); + config, event_callback)); } void JingleHostConnection::Disconnect() { @@ -105,20 +103,19 @@ void JingleHostConnection::OnNewConnection( } MessageLoop* JingleHostConnection::message_loop() { - return network_thread_->message_loop(); + return context_->jingle_thread()->message_loop(); } -void JingleHostConnection::DoConnect(const std::string& username, - const std::string& auth_token, - const std::string& host_jid, +void JingleHostConnection::DoConnect(ClientConfig* config, HostEventCallback* event_callback) { DCHECK_EQ(message_loop(), MessageLoop::current()); event_callback_ = event_callback; - jingle_client_ = new JingleClient(network_thread_); - jingle_client_->Init(username, auth_token, kChromotingTokenServiceName, this); - jingle_channel_ = jingle_client_->Connect(host_jid, this); + jingle_client_ = new JingleClient(context_->jingle_thread()); + jingle_client_->Init(config->username(), config->auth_token(), + kChromotingTokenServiceName, this); + jingle_channel_ = jingle_client_->Connect(config->host_jid(), this); } void JingleHostConnection::DoDisconnect() { diff --git a/remoting/client/jingle_host_connection.h b/remoting/client/jingle_host_connection.h index 96178aa..5fc75cb 100644 --- a/remoting/client/jingle_host_connection.h +++ b/remoting/client/jingle_host_connection.h @@ -22,6 +22,7 @@ #include "base/scoped_ptr.h" #include "base/task.h" #include "remoting/base/protocol_decoder.h" +#include "remoting/client/client_context.h" #include "remoting/client/host_connection.h" #include "remoting/jingle_glue/jingle_channel.h" #include "remoting/jingle_glue/jingle_client.h" @@ -30,18 +31,17 @@ class MessageLoop; namespace remoting { +class ClientConfig; class JingleThread; class JingleHostConnection : public HostConnection, public JingleChannel::Callback, public JingleClient::Callback { public: - explicit JingleHostConnection(JingleThread* network_thread); + explicit JingleHostConnection(ClientContext* context); virtual ~JingleHostConnection(); - virtual void Connect(const std::string& username, - const std::string& auth_token, - const std::string& host_jid, + virtual void Connect(ClientConfig* config, HostEventCallback* event_callback); virtual void Disconnect(); @@ -61,13 +61,11 @@ class JingleHostConnection : public HostConnection, private: MessageLoop* message_loop(); - void DoConnect(const std::string& username, - const std::string& auth_token, - const std::string& host_jid, + void DoConnect(ClientConfig* config, HostEventCallback* event_callback); void DoDisconnect(); - JingleThread* network_thread_; + ClientContext* context_; scoped_refptr<JingleClient> jingle_client_; scoped_refptr<JingleChannel> jingle_channel_; diff --git a/remoting/client/plugin/chromoting_plugin.cc b/remoting/client/plugin/chromoting_plugin.cc index 160bf3a..bb1aa80 100644 --- a/remoting/client/plugin/chromoting_plugin.cc +++ b/remoting/client/plugin/chromoting_plugin.cc @@ -10,18 +10,18 @@ #include "base/message_loop.h" #include "base/string_util.h" #include "base/thread.h" +#include "remoting/client/client_config.h" +#include "remoting/client/client_util.h" #include "remoting/client/chromoting_client.h" #include "remoting/client/host_connection.h" #include "remoting/client/jingle_host_connection.h" +#include "remoting/client/plugin/pepper_input_handler.h" #include "remoting/client/plugin/pepper_view.h" #include "remoting/jingle_glue/jingle_thread.h" #include "third_party/ppapi/c/pp_event.h" #include "third_party/ppapi/c/pp_rect.h" #include "third_party/ppapi/cpp/completion_callback.h" -using std::string; -using std::vector; - namespace remoting { const char* ChromotingPlugin::kMimeType = "pepper-application/x-chromoting"; @@ -32,18 +32,17 @@ ChromotingPlugin::ChromotingPlugin(PP_Instance pp_instance) } ChromotingPlugin::~ChromotingPlugin() { - if (host_connection_.get()) - host_connection_->Disconnect(); + if (client_.get()) { + client_->Stop(); + } // TODO(ajwong): We need to ensure all objects have actually stopped posting // to the message loop before this point. Right now, we don't have a well // defined stop for the plugin process, and the thread shutdown is likely a // race condition. - if (network_thread_.get()) - network_thread_->Stop(); - - if (main_thread_.get()) - main_thread_->Stop(); + if (context_.get()) { + context_->Stop(); + } } bool ChromotingPlugin::Init(uint32_t argc, @@ -77,34 +76,29 @@ bool ChromotingPlugin::Init(uint32_t argc, return false; } - string user_id; - string auth_token; - string host_jid; - if (!ParseUrl(url, &user_id, &auth_token, &host_jid)) { + ClientConfig config; + if (!GetLoginInfoFromUrlParams(url, &config)) { LOG(WARNING) << "Could not parse URL: " << url; return false; } - // Start the threads. - main_thread_.reset(new base::Thread("ChromoClientMain")); - if (!main_thread_->Start()) { - LOG(ERROR) << "Main thread failed to start."; - return false; - } - network_thread_.reset(new JingleThread()); - network_thread_->Start(); - - // Create the chromting objects. - host_connection_.reset(new JingleHostConnection(network_thread_.get())); + // Create the chromoting objects. + host_connection_.reset(new JingleHostConnection(context_.get())); view_.reset(new PepperView(this)); - client_.reset(new ChromotingClient(main_thread_->message_loop(), - host_connection_.get(), view_.get())); + input_handler_.reset(new PepperInputHandler()); + client_.reset(new ChromotingClient(&config, + context_.get(), + host_connection_.get(), + view_.get(), + input_handler_.get(), + NULL)); // Default to a medium grey. view_->SetSolidFill(0xFFCDCDCD); // Kick off the connection. - host_connection_->Connect(user_id, auth_token, host_jid, client_.get()); + context_->Start(); + client_->Start(); return true; } @@ -153,41 +147,4 @@ bool ChromotingPlugin::HandleEvent(const PP_Event& event) { return false; } -bool ChromotingPlugin::ParseUrl(const std::string& url, - string* user_id, - string* auth_token, - string* host_jid) { - // TODO(ajwong): We should use GURL or something. Don't parse this by hand! - - // The Url should be of the form: - // - // chromotocol://<hostid>?user=<userid>&auth=<authtoken>&jid=<hostjid> - // - vector<string> parts; - SplitString(url, '&', &parts); - if (parts.size() != 3) { - return false; - } - - size_t pos = parts[0].rfind('='); - if (pos == string::npos && (pos + 1) != string::npos) { - return false; - } - user_id->assign(parts[0].substr(pos + 1)); - - pos = parts[1].rfind('='); - if (pos == string::npos && (pos + 1) != string::npos) { - return false; - } - auth_token->assign(parts[1].substr(pos + 1)); - - pos = parts[2].rfind('='); - if (pos == string::npos && (pos + 1) != string::npos) { - return false; - } - host_jid->assign(parts[2].substr(pos + 1)); - - return true; -} - } // namespace remoting diff --git a/remoting/client/plugin/chromoting_plugin.h b/remoting/client/plugin/chromoting_plugin.h index 0aac051..3bd0470 100644 --- a/remoting/client/plugin/chromoting_plugin.h +++ b/remoting/client/plugin/chromoting_plugin.h @@ -34,7 +34,9 @@ class Module; namespace remoting { class ChromotingClient; +class ClientContext; class HostConnection; +class InputHandler; class JingleThread; class PepperView; @@ -56,11 +58,6 @@ class ChromotingPlugin : public pp::Instance { FRIEND_TEST(ChromotingPluginTest, ParseUrl); FRIEND_TEST(ChromotingPluginTest, TestCaseSetup); - static bool ParseUrl(const std::string& url, - std::string* user_id, - std::string* auth_token, - std::string* host_jid); - // Since we're an internal plugin, we can just grab the message loop during // init to figure out which thread we're on. This should only be used to // sanity check which thread we're executing on. Do not post task here! @@ -69,11 +66,14 @@ class ChromotingPlugin : public pp::Instance { // TODO(ajwong): Think if there is a better way to safeguard this. MessageLoop* pepper_main_loop_dont_post_to_me_; - scoped_ptr<base::Thread> main_thread_; - scoped_ptr<JingleThread> network_thread_; + scoped_ptr<ClientContext> context_; scoped_ptr<HostConnection> host_connection_; + scoped_ptr<PepperView> view_; + + scoped_ptr<InputHandler> input_handler_; + scoped_ptr<ChromotingClient> client_; DISALLOW_COPY_AND_ASSIGN(ChromotingPlugin); diff --git a/remoting/client/plugin/chromoting_plugin_unittest.cc b/remoting/client/plugin/chromoting_plugin_unittest.cc index edda0e3..2dc0358 100644 --- a/remoting/client/plugin/chromoting_plugin_unittest.cc +++ b/remoting/client/plugin/chromoting_plugin_unittest.cc @@ -17,17 +17,4 @@ class ChromotingPluginTest : public testing::Test { } }; -TEST_F(ChromotingPluginTest, ParseUrl) { - const char url[] = "chromotocol://hostid?user=auser&auth=someauth&jid=ajid"; - std::string user_id; - std::string auth_token; - std::string host_jid; - ASSERT_TRUE( - ChromotingPlugin::ParseUrl(url, &user_id, &auth_token, &host_jid)); - - EXPECT_EQ("auser", user_id); - EXPECT_EQ("someauth", auth_token); - EXPECT_EQ("ajid", host_jid); -} - } // namespace remoting diff --git a/remoting/client/plugin/pepper_input_handler.cc b/remoting/client/plugin/pepper_input_handler.cc new file mode 100644 index 0000000..53cced3 --- /dev/null +++ b/remoting/client/plugin/pepper_input_handler.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/client/plugin/pepper_input_handler.h" + +namespace remoting { + +PepperInputHandler::PepperInputHandler() { +} + +PepperInputHandler::~PepperInputHandler() { +} + +void PepperInputHandler::Initialize() { + // TODO(garykac): Implement this. +} + +} // namespace remoting diff --git a/remoting/client/plugin/pepper_input_handler.h b/remoting/client/plugin/pepper_input_handler.h new file mode 100644 index 0000000..59399e9 --- /dev/null +++ b/remoting/client/plugin/pepper_input_handler.h @@ -0,0 +1,27 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_CLIENT_PLUGIN_PEPPER_INPUT_HANDLER_H_ +#define REMOTING_CLIENT_PLUGIN_PEPPER_INPUT_HANDLER_H_ + +#include "remoting/client/input_handler.h" + +namespace remoting { + +class PepperInputHandler : public InputHandler { + public: + PepperInputHandler(); + virtual ~PepperInputHandler(); + + void Initialize(); + + private: + DISALLOW_COPY_AND_ASSIGN(PepperInputHandler); +}; + +} // namespace remoting + +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::PepperInputHandler); + +#endif // REMOTING_CLIENT_PLUGIN_PEPPER_INPUT_HANDLER_H_ diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc index a6ebc13..ce38289 100644 --- a/remoting/client/plugin/pepper_view.cc +++ b/remoting/client/plugin/pepper_view.cc @@ -30,6 +30,13 @@ PepperView::PepperView(ChromotingPlugin* plugin) PepperView::~PepperView() { } +bool PepperView::Initialize() { + return true; +} + +void PepperView::TearDown() { +} + void PepperView::Paint() { if (!plugin_->CurrentlyOnPluginThread()) { RunTaskOnPluginThread(NewRunnableMethod(this, &PepperView::Paint)); @@ -118,10 +125,10 @@ void PepperView::SetViewport(int x, int y, int width, int height) { } } -void PepperView::SetBackingStoreSize(int width, int height) { +void PepperView::SetHostScreenSize(int width, int height) { if (!plugin_->CurrentlyOnPluginThread()) { RunTaskOnPluginThread(NewRunnableMethod(this, - &PepperView::SetBackingStoreSize, + &PepperView::SetHostScreenSize, width, height)); return; } diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h index 655b9ae..3622ab3 100644 --- a/remoting/client/plugin/pepper_view.h +++ b/remoting/client/plugin/pepper_view.h @@ -37,11 +37,13 @@ class PepperView : public ChromotingView { virtual ~PepperView(); // ChromotingView implementation. + virtual bool Initialize(); + virtual void TearDown(); virtual void Paint(); virtual void SetSolidFill(uint32 color); virtual void UnsetSolidFill(); virtual void SetViewport(int x, int y, int width, int height); - virtual void SetBackingStoreSize(int width, int height); + virtual void SetHostScreenSize(int width, int height); virtual void HandleBeginUpdateStream(HostMessage* msg); virtual void HandleUpdateStreamPacket(HostMessage* msg); virtual void HandleEndUpdateStream(HostMessage* msg); diff --git a/remoting/client/simple_client.cc b/remoting/client/simple_client.cc deleted file mode 100644 index d1dd6e2..0000000 --- a/remoting/client/simple_client.cc +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// A simple client implements a minimal Chromoting client and shows -// network traffic for debugging. - -#include <iostream> -#include <list> - -#include "base/at_exit.h" -#include "base/message_loop.h" -#include "base/stl_util-inl.h" - -#include "base/waitable_event.h" -#include "media/base/data_buffer.h" -#include "remoting/base/protocol_decoder.h" -#include "remoting/client/client_util.h" -#include "remoting/client/jingle_host_connection.h" -#include "remoting/jingle_glue/jingle_client.h" -#include "remoting/jingle_glue/jingle_thread.h" - -using remoting::BeginUpdateStreamMessage; -using remoting::EndUpdateStreamMessage; -using remoting::HostConnection; -using remoting::HostMessage; -using remoting::HostMessageList; -using remoting::InitClientMessage; -using remoting::JingleClient; -using remoting::JingleHostConnection; -using remoting::JingleThread; -using remoting::ProtocolDecoder; -using remoting::UpdateStreamPacketMessage; - -class SimpleHostEventCallback : public HostConnection::HostEventCallback { - public: - explicit SimpleHostEventCallback(MessageLoop* loop, - base::WaitableEvent* client_done) - : main_loop_(loop), client_done_(client_done) { - } - - virtual void HandleMessages(HostConnection* conn, - HostMessageList* messages) { - HostMessageList list; - messages->swap(list); - for (size_t i = 0; i < list.size(); ++i) { - HostMessage* msg = list[i]; - if (msg->has_init_client()) { - HandleInitClientMessage(msg); - } else if (msg->has_begin_update_stream()) { - HandleBeginUpdateStreamMessage(msg); - } else if (msg->has_update_stream_packet()) { - HandleUpdateStreamPacketMessage(msg); - } else if (msg->has_end_update_stream()) { - HandleEndUpdateStreamMessage(msg); - } - } - STLDeleteElements<HostMessageList>(&list); - } - - virtual void OnConnectionOpened(HostConnection* conn) { - std::cout << "Connection establised." << std::endl; - } - - virtual void OnConnectionClosed(HostConnection* conn) { - std::cout << "Connection closed." << std::endl; - client_done_->Signal(); - } - - virtual void OnConnectionFailed(HostConnection* conn) { - std::cout << "Conection failed." << std::endl; - client_done_->Signal(); - } - - private: - void HandleInitClientMessage(HostMessage* host_msg) { - const InitClientMessage& msg = host_msg->init_client(); - std::cout << "InitClient (" << msg.width() - << ", " << msg.height() << ")" << std::endl; - } - - void HandleBeginUpdateStreamMessage(HostMessage* host_msg) { - const BeginUpdateStreamMessage& msg = host_msg->begin_update_stream(); - std::cout << msg.GetTypeName() << std::endl; - } - - void HandleUpdateStreamPacketMessage(HostMessage* host_msg) { - const UpdateStreamPacketMessage& msg = host_msg->update_stream_packet(); - if (!msg.has_begin_rect()) - return; - - std::cout << "UpdateStreamPacket (" << msg.begin_rect().x() - << ", " << msg.begin_rect().y() << ") [" - << msg.begin_rect().width() << " x " - << msg.begin_rect().height() << "]" - << std::endl; - } - - void HandleEndUpdateStreamMessage(HostMessage* host_msg) { - const EndUpdateStreamMessage& msg = host_msg->end_update_stream(); - std::cout << msg.GetTypeName() << std::endl; - } - - // JingleClient::Callback interface. - void OnStateChange(JingleClient* client, JingleClient::State state) { - if (state == JingleClient::CONNECTED) { - LOG(INFO) << "Connected as: " << client->GetFullJid(); - } else if (state == JingleClient::CLOSED) { - LOG(INFO) << "Connection closed."; - } - } - - MessageLoop* main_loop_; - base::WaitableEvent* client_done_; - - DISALLOW_COPY_AND_ASSIGN(SimpleHostEventCallback); -}; - -int main(int argc, char** argv) { - base::AtExitManager exit_manager; - std::string host_jid; - std::string username; - std::string auth_token; - - if (!remoting::GetLoginInfo(argc, argv, &host_jid, &username, &auth_token)) { - std::cerr << "Cannot get valid login info." << std::endl; - return 1; - } - - // Start up a thread to run everything. - JingleThread network_thread; - network_thread.Start(); - - base::WaitableEvent client_done(false, false); - SimpleHostEventCallback handler(network_thread.message_loop(), &client_done); - JingleHostConnection connection(&network_thread); - connection.Connect(username, auth_token, host_jid, &handler); - - // Wait until the mainloop has been signaled to exit. - client_done.Wait(); - - connection.Disconnect(); - network_thread.message_loop()->PostTask(FROM_HERE, - new MessageLoop::QuitTask()); - network_thread.Stop(); - - return 0; -} diff --git a/remoting/client/x11_client.cc b/remoting/client/x11_client.cc index eeda70d..8d79f92 100644 --- a/remoting/client/x11_client.cc +++ b/remoting/client/x11_client.cc @@ -2,259 +2,46 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// This file implements a X11 chromoting client. +// This file implements a simple X11 chromoting client. #include <iostream> #include "base/at_exit.h" -#include "base/message_loop.h" -#include "base/stl_util-inl.h" -#include "base/task.h" -#include "base/waitable_event.h" +#include "remoting/client/chromoting_client.h" +#include "remoting/client/client_config.h" #include "remoting/client/client_util.h" -#include "remoting/client/host_connection.h" #include "remoting/client/jingle_host_connection.h" -#include "remoting/jingle_glue/jingle_thread.h" - -// Include Xlib at the end because it clashes with ClientMessage defined in -// the protocol buffer. -// x11_view.h also includes Xlib.h so put it behind all other headers but -// before Xlib.h #include "remoting/client/x11_view.h" -#include <X11/Xlib.h> - -using remoting::JingleHostConnection; -using remoting::JingleThread; - -namespace remoting { - -class X11Client : public HostConnection::HostEventCallback { - public: - X11Client(MessageLoop* loop, base::WaitableEvent* client_done) - : message_loop_(loop), - client_done_(client_done), - display_(NULL), - window_(0), - width_(0), - height_(0) { - } - - virtual ~X11Client() { - DCHECK(!display_); - DCHECK(!window_); - } - - //////////////////////////////////////////////////////////////////////////// - // HostConnection::EventHandler implementations. - virtual void HandleMessages(HostConnection* conn, - remoting::HostMessageList* messages) { - for (size_t i = 0; i < messages->size(); ++i) { - HostMessage* msg = (*messages)[i]; - if (msg->has_init_client()) { - message_loop_->PostTask( - FROM_HERE, NewRunnableMethod(this, &X11Client::DoInitClient, msg)); - } else if (msg->has_begin_update_stream()) { - message_loop_->PostTask( - FROM_HERE, - NewRunnableMethod(this, &X11Client::DoBeginUpdate, msg)); - } else if (msg->has_update_stream_packet()) { - message_loop_->PostTask( - FROM_HERE, - NewRunnableMethod(this, &X11Client::DoHandleUpdate, msg)); - } else if (msg->has_end_update_stream()) { - message_loop_->PostTask( - FROM_HERE, NewRunnableMethod(this, &X11Client::DoEndUpdate, msg)); - } else { - NOTREACHED() << "Unknown message received"; - } - } - // Assume we have processed all the messages. - messages->clear(); - } - - virtual void OnConnectionOpened(HostConnection* conn) { - std::cout << "Connection established." << std::endl; - } - - virtual void OnConnectionClosed(HostConnection* conn) { - std::cout << "Connection closed." << std::endl; - client_done_->Signal(); - } - - virtual void OnConnectionFailed(HostConnection* conn) { - std::cout << "Conection failed." << std::endl; - client_done_->Signal(); - } - - void InitX11() { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &X11Client::DoInitX11)); - } - - void DoInitX11() { - display_ = XOpenDisplay(NULL); - if (!display_) { - std::cout << "Error - cannot open display" << std::endl; - client_done_->Signal(); - return; - } - - // Get properties of the screen. - int screen = DefaultScreen(display_); - int root_window = RootWindow(display_, screen); - - // Creates the window. - window_ = XCreateSimpleWindow(display_, root_window, 1, 1, 640, 480, 0, - BlackPixel(display_, screen), - BlackPixel(display_, screen)); - DCHECK(window_); - XStoreName(display_, window_, "X11 Remoting"); - - // Specifies what kind of messages we want to receive. - XSelectInput(display_, window_, ExposureMask | ButtonPressMask); - XMapWindow(display_, window_); - } - - void DestroyX11() { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &X11Client::DoDestroyX11)); - } - - void DoDestroyX11() { - if (display_ && window_) { - // Shutdown the window system. - XDestroyWindow(display_, window_); - XCloseDisplay(display_); - display_ = NULL; - window_ = 0; - } - } - - private: - // This method is executed on the main loop. - void DoInitClient(HostMessage* msg) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(msg->has_init_client()); - scoped_ptr<HostMessage> deleter(msg); - - // Saves the dimension and resize the window. - width_ = msg->init_client().width(); - height_ = msg->init_client().height(); - LOG(INFO) << "Init client received: " << width_ << "x" << height_; - XResizeWindow(display_, window_, width_, height_); +#include "remoting/client/x11_input_handler.h" - // Now construct the X11View that renders the remote desktop. - view_.reset(new X11View(display_, window_, width_, height_)); - - // Schedule the event handler to process the event queue of X11. - ScheduleX11EventHandler(); - } - - // The following methods are executed on the same thread as libjingle. - void DoBeginUpdate(HostMessage* msg) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(msg->has_begin_update_stream()); - - view_->HandleBeginUpdateStream(msg); - } - - void DoHandleUpdate(HostMessage* msg) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(msg->has_update_stream_packet()); - - view_->HandleUpdateStreamPacket(msg); - } - - void DoEndUpdate(HostMessage* msg) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(msg->has_end_update_stream()); - - view_->HandleEndUpdateStream(msg); - } - - void DoProcessX11Events() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - if (XPending(display_)) { - XEvent e; - XNextEvent(display_, &e); - if (e.type == Expose) { - // Tell the ChromotingView to paint again. - view_->Paint(); - } else if (e.type == ButtonPress) { - // TODO(hclam): Implement. - NOTIMPLEMENTED(); - } else { - // TODO(hclam): Implement. - NOTIMPLEMENTED(); - } - } - - // Schedule the next event handler. - ScheduleX11EventHandler(); - } - - void ScheduleX11EventHandler() { - // Schedule a delayed task to process X11 events in 10ms. - static const int kProcessEventsInterval = 10; - message_loop_->PostDelayedTask( - FROM_HERE, - NewRunnableMethod(this, &X11Client::DoProcessX11Events), - kProcessEventsInterval); - } - - MessageLoop* message_loop_; - base::WaitableEvent* client_done_; - - // Members used for display. - Display* display_; - Window window_; - - // Dimension of the window. They are initialized when InitClient message is - // received. - int width_; - int height_; - - scoped_ptr<ChromotingView> view_; - - DISALLOW_COPY_AND_ASSIGN(X11Client); -}; - -} // namespace remoting - -DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::X11Client); +void ClientQuit(MessageLoop* loop) { + loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} int main(int argc, char** argv) { base::AtExitManager at_exit; - std::string host_jid; - std::string username; - std::string auth_token; - if (!remoting::GetLoginInfo(argc, argv, &host_jid, &username, &auth_token)) { - std::cout << "Cannot obtain login info" << std::endl; + remoting::ClientConfig config; + if (!remoting::GetLoginInfoFromArgs(argc, argv, &config)) { + std::cout << "Unable to obtain login info" << std::endl; return 1; } - JingleThread network_thread; - network_thread.Start(); - - base::WaitableEvent client_done(false, false); - remoting::X11Client client(network_thread.message_loop(), &client_done); - JingleHostConnection connection(&network_thread); - connection.Connect(username, auth_token, host_jid, &client); - - client.InitX11(); - - // Wait until the main loop is done. - client_done.Wait(); - - connection.Disconnect(); + MessageLoop ui_loop; + remoting::ClientContext context; + remoting::JingleHostConnection connection(&context); + remoting::X11View view; + remoting::X11InputHandler input_handler(&context, &view); + remoting::ChromotingClient client(&config, &context, &connection, &view, + &input_handler, NewRunnableFunction(&ClientQuit, &ui_loop)); - client.DestroyX11(); + // Run the client on a new MessageLoop until + context.Start(); + client.Start(); + ui_loop.Run(); - // Quit the current message loop. - network_thread.message_loop()->PostTask(FROM_HERE, - new MessageLoop::QuitTask()); - network_thread.Stop(); + client.Stop(); + context.Stop(); return 0; } diff --git a/remoting/client/x11_input_handler.cc b/remoting/client/x11_input_handler.cc new file mode 100644 index 0000000..455ff61 --- /dev/null +++ b/remoting/client/x11_input_handler.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/client/x11_input_handler.h" + +#include "base/message_loop.h" +#include "remoting/client/client_context.h" +#include "remoting/client/x11_view.h" +#include "remoting/jingle_glue/jingle_thread.h" + +// Include Xlib at the end because it clashes with ClientMessage defined in +// the protocol buffer. +#include <X11/Xlib.h> + +namespace remoting { + +X11InputHandler::X11InputHandler(ClientContext* context, + ChromotingView* view) + : context_(context), + view_(view) { +} + +X11InputHandler::~X11InputHandler() { +} + +void X11InputHandler::Initialize() { + ScheduleX11EventHandler(); +} + +void X11InputHandler::DoProcessX11Events() { + DCHECK_EQ(context_->jingle_thread()->message_loop(), MessageLoop::current()); + Display* display = static_cast<X11View*>(view_)->display(); + if (XPending(display)) { + XEvent e; + XNextEvent(display, &e); + if (e.type == Expose) { + // Tell the view to paint again. + view_->Paint(); + } else if (e.type == ButtonPress) { + // TODO(hclam): Implement. + NOTIMPLEMENTED(); + } else { + // TODO(hclam): Implement. + NOTIMPLEMENTED(); + } + } + + // Schedule the next event handler. + ScheduleX11EventHandler(); +} + +void X11InputHandler::ScheduleX11EventHandler() { + // Schedule a delayed task to process X11 events in 10ms. + static const int kProcessEventsInterval = 10; + context_->jingle_thread()->message_loop()->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &X11InputHandler::DoProcessX11Events), + kProcessEventsInterval); +} + +} // namespace remoting diff --git a/remoting/client/x11_input_handler.h b/remoting/client/x11_input_handler.h new file mode 100644 index 0000000..f4b4471 --- /dev/null +++ b/remoting/client/x11_input_handler.h @@ -0,0 +1,38 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_CLIENT_X11_INPUT_HANDLER_H_ +#define REMOTING_CLIENT_X11_INPUT_HANDLER_H_ + +#include "remoting/client/input_handler.h" + +namespace remoting { + +class ClientContext; +class ChromotingView; + +class X11InputHandler : public InputHandler { + public: + X11InputHandler(ClientContext* context, ChromotingView* view); + virtual ~X11InputHandler(); + + void Initialize(); + + private: + + void DoProcessX11Events(); + + void ScheduleX11EventHandler(); + + ClientContext* context_; + ChromotingView* view_; + + DISALLOW_COPY_AND_ASSIGN(X11InputHandler); +}; + +} // namespace remoting + +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::X11InputHandler); + +#endif // REMOTING_CLIENT_X11_INPUT_HANDLER_H_ diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc index c5f9a88..e4b59ae 100644 --- a/remoting/client/x11_view.cc +++ b/remoting/client/x11_view.cc @@ -14,6 +14,14 @@ namespace remoting { +X11View::X11View() + : display_(NULL), + window_(0), + width_(0), + height_(0), + picture_(0) { +} + X11View::X11View(Display* display, XID window, int width, int height) : display_(display), window_(window), @@ -26,9 +34,49 @@ X11View::X11View(Display* display, XID window, int width, int height) } X11View::~X11View() { + DCHECK(!display_); + DCHECK(!window_); +} + +bool X11View::Initialize() { + display_ = XOpenDisplay(NULL); + if (!display_) { + return false; + } + + // Get properties of the screen. + int screen = DefaultScreen(display_); + int root_window = RootWindow(display_, screen); + + // Creates the window. + window_ = XCreateSimpleWindow(display_, root_window, 1, 1, 640, 480, 0, + BlackPixel(display_, screen), + BlackPixel(display_, screen)); + DCHECK(window_); + XStoreName(display_, window_, "X11 Remoting"); + + // Specifies what kind of messages we want to receive. + XSelectInput(display_, window_, ExposureMask | ButtonPressMask); + XMapWindow(display_, window_); + return true; +} + +void X11View::TearDown() { + if (display_ && window_) { + // Shutdown the window system. + XDestroyWindow(display_, window_); + XCloseDisplay(display_); + } + display_ = NULL; + window_ = 0; } void X11View::Paint() { + // Don't bother attempting to paint if the display hasn't been set up. + if (!display_ || !window_ || !height_ || !width_) { + return; + } + // TODO(hclam): Paint only the updated regions. all_update_rects_.clear(); @@ -80,23 +128,24 @@ void X11View::Paint() { } void X11View::SetSolidFill(uint32 color) { - // TODO(ajwong): Implement. - NOTIMPLEMENTED(); + // TODO(garykac): Implement. + // NOTIMPLEMENTED(); } void X11View::UnsetSolidFill() { - // TODO(ajwong): Implement. - NOTIMPLEMENTED(); + // TODO(garykac): Implement. + // NOTIMPLEMENTED(); } void X11View::SetViewport(int x, int y, int width, int height) { - // TODO(ajwong): Implement. - NOTIMPLEMENTED(); + // TODO(garykac): Implement. + // NOTIMPLEMENTED(); } -void X11View::SetBackingStoreSize(int width, int height) { - // TODO(ajwong): Implement. - NOTIMPLEMENTED(); +void X11View::SetHostScreenSize(int width, int height) { + width_ = width; + height_ = height; + XResizeWindow(display_, window_, width_, height_); } void X11View::InitPaintTarget() { diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h index 1a76cb3..73e63c7 100644 --- a/remoting/client/x11_view.h +++ b/remoting/client/x11_view.h @@ -19,20 +19,25 @@ namespace remoting { // A ChromotingView implemented using X11 and XRender. class X11View : public ChromotingView { public: + X11View(); // Delete this. X11View(Display* display, XID window, int width, int height); virtual ~X11View(); // ChromotingView implementations. + virtual bool Initialize(); + virtual void TearDown(); virtual void Paint(); virtual void SetSolidFill(uint32 color); virtual void UnsetSolidFill(); virtual void SetViewport(int x, int y, int width, int height); - virtual void SetBackingStoreSize(int width, int height); + virtual void SetHostScreenSize(int width, int height); virtual void HandleBeginUpdateStream(HostMessage* msg); virtual void HandleUpdateStreamPacket(HostMessage* msg); virtual void HandleEndUpdateStream(HostMessage* msg) ; + Display* display() { return display_; } + private: void InitPaintTarget(); void OnPartialDecodeDone(); diff --git a/remoting/host/chromoting_host_context.h b/remoting/host/chromoting_host_context.h index f488f74..f4e2f8a 100644 --- a/remoting/host/chromoting_host_context.h +++ b/remoting/host/chromoting_host_context.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef REMOTING_CHROMOTING_HOST_CONTEXT_H_ -#define REMOTING_CHROMOTING_HOST_CONTEXT_H_ +#ifndef REMOTING_HOST_CHROMOTING_HOST_CONTEXT_H_ +#define REMOTING_HOST_CHROMOTING_HOST_CONTEXT_H_ #include <string> @@ -31,16 +31,16 @@ class ChromotingHostContext { private: FRIEND_TEST(ChromotingHostContextTest, StartAndStop); - // A thread that host network operations. + // A thread that hosts all network operations. JingleThread jingle_thread_; - // A thread that host ChromotingHost. + // A thread that hosts ChromotingHost and performs rate control. base::Thread main_thread_; - // A thread that host all capture operations. + // A thread that hosts all capture operations. base::Thread capture_thread_; - // A thread that host all encode operations. + // A thread that hosts all encode operations. base::Thread encode_thread_; DISALLOW_COPY_AND_ASSIGN(ChromotingHostContext); diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index bda9e60..85718e3 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -51,6 +51,8 @@ }, 'sources': [ 'client/x11_client.cc', + 'client/x11_input_handler.cc', + 'client/x11_input_handler.h', 'client/x11_view.cc', 'client/x11_view.h', ], @@ -79,6 +81,8 @@ 'client/plugin/chromoting_plugin.h', 'client/plugin/pepper_entrypoints.cc', 'client/plugin/pepper_entrypoints.h', + 'client/plugin/pepper_input_handler.cc', + 'client/plugin/pepper_input_handler.h', 'client/plugin/pepper_view.cc', 'client/plugin/pepper_view.h', 'client/plugin/pepper_util.cc', @@ -222,12 +226,16 @@ 'client/chromoting_client.cc', 'client/chromoting_client.h', 'client/chromoting_view.h', + 'client/client_config.h', + 'client/client_context.cc', + 'client/client_context.h', 'client/client_util.cc', 'client/client_util.h', 'client/decoder.h', 'client/decoder_verbatim.cc', 'client/decoder_verbatim.h', 'client/host_connection.h', + 'client/input_handler.h', 'client/jingle_host_connection.cc', 'client/jingle_host_connection.h', ], @@ -263,20 +271,7 @@ 'sources': [ 'host/keygen_main.cc', ], - }, # end of target 'chromoting_simple_host' - - { - 'target_name': 'chromoting_simple_client', - 'type': 'executable', - 'dependencies': [ - 'chromoting_base', - 'chromoting_client', - 'chromoting_jingle_glue', - ], - 'sources': [ - 'client/simple_client.cc', - ], - }, # end of target 'chromoting_simple_client' + }, # end of target 'chromoting_host_keygen' { 'target_name': 'chromoting_jingle_glue', |