diff options
Diffstat (limited to 'remoting/client')
-rw-r--r-- | remoting/client/chromoting_view.h | 37 | ||||
-rw-r--r-- | remoting/client/client_util.cc | 76 | ||||
-rw-r--r-- | remoting/client/client_util.h | 19 | ||||
-rw-r--r-- | remoting/client/decoder.h | 43 | ||||
-rw-r--r-- | remoting/client/decoder_verbatim.cc | 56 | ||||
-rw-r--r-- | remoting/client/decoder_verbatim.h | 16 | ||||
-rw-r--r-- | remoting/client/host_connection.cc | 16 | ||||
-rw-r--r-- | remoting/client/simple_client.cc | 84 | ||||
-rw-r--r-- | remoting/client/x11_client.cc | 262 | ||||
-rw-r--r-- | remoting/client/x11_view.cc | 151 | ||||
-rw-r--r-- | remoting/client/x11_view.h | 56 |
11 files changed, 683 insertions, 133 deletions
diff --git a/remoting/client/chromoting_view.h b/remoting/client/chromoting_view.h new file mode 100644 index 0000000..cf70b99 --- /dev/null +++ b/remoting/client/chromoting_view.h @@ -0,0 +1,37 @@ +// 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_CHROMOTING_VIEW_H_ +#define REMOTING_CLIENT_CHROMOTING_VIEW_H_ + +#include "base/ref_counted.h" + +namespace remoting { + +class HostMessage; + +// ChromotingView defines the behavior of an object that draws a view of the +// remote desktop. Its main function is to choose the right decoder and render +// the update stream onto the screen. +class ChromotingView : public base::RefCountedThreadSafe<ChromotingView> { + public: + virtual ~ChromotingView() {} + + // Tells the ChromotingView to paint the current image on the screen. + // TODO(hclam): Add rects as parameter if needed. + virtual void Paint() = 0; + + // Handle the BeginUpdateStream message. + virtual void HandleBeginUpdateStream(HostMessage* msg) = 0; + + // Handle the UpdateStreamPacket message. + virtual void HandleUpdateStreamPacket(HostMessage* msg) = 0; + + // Handle the EndUpdateStream message. + virtual void HandleEndUpdateStream(HostMessage* msg) = 0; +}; + +} // namespace remoting + +#endif // REMOTING_CLIENT_CHROMOTING_VIEW_H_ diff --git a/remoting/client/client_util.cc b/remoting/client/client_util.cc new file mode 100644 index 0000000..66906f8 --- /dev/null +++ b/remoting/client/client_util.cc @@ -0,0 +1,76 @@ +// 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_util.h" + +#include <iostream> + +static void SetConsoleEcho(bool on) { +#ifdef WIN32 + HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); + if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) + return; + + DWORD mode; + if (!GetConsoleMode(hIn, &mode)) + return; + + if (on) { + mode = mode | ENABLE_ECHO_INPUT; + } else { + mode = mode & ~ENABLE_ECHO_INPUT; + } + + SetConsoleMode(hIn, mode); +#else + if (on) + system("stty echo"); + else + system("stty -echo"); +#endif +} + +namespace remoting { + +// Get host JID from command line arguments, or stdin if not specified. +bool GetLoginInfo(std::string& host_jid, + std::string& username, + std::string& password) { + std::cout << "Host JID: "; + std::cin >> host_jid; + std::cin.ignore(); // Consume the leftover '\n' + + if (host_jid.find("/chromoting") == std::string::npos) { + std::cerr << "Error: Expected Host JID in format: <jid>/chromoting<id>" + << std::endl; + return false; + } + + // 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 = default_username; + } + if (username.length() == 0) { + std::cerr << "Error: Expected valid JID username" << std::endl; + return 1; + } + + // Get password (with console echo turned off). + SetConsoleEcho(false); + std::cout << "Password: "; + getline(std::cin, password); + SetConsoleEcho(true); + std::cout << std::endl; + return true; +} + +} // namespace remoting diff --git a/remoting/client/client_util.h b/remoting/client/client_util.h new file mode 100644 index 0000000..4c2902e --- /dev/null +++ b/remoting/client/client_util.h @@ -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. + +#ifndef REMOTING_CLIENT_CLIENT_UTIL_H_ +#define REMOTING_CLIENT_CLIENT_UTIL_H_ + +#include <string> + +namespace remoting { + +// Get the login info from the console and writes into |host_jid|, |username| +// and |password|. Return true if successful. +bool GetLoginInfo(std::string& host_jid, std::string& username, + std::string& password); + +} // namespace remoting + +#endif // REMOTING_CLIENT_CLIENT_UTIL_H_ diff --git a/remoting/client/decoder.h b/remoting/client/decoder.h index d39be07..ac2fe4d 100644 --- a/remoting/client/decoder.h +++ b/remoting/client/decoder.h @@ -7,7 +7,7 @@ #include <vector> -#include "base/callback.h" +#include "base/task.h" #include "base/scoped_ptr.h" #include "gfx/rect.h" #include "media/base/video_frame.h" @@ -15,12 +15,15 @@ namespace remoting { +// TODO(hclam): Merge this with the one in remoting/host/encoder.h. +typedef std::vector<gfx::Rect> UpdatedRects; + // Defines the behavior of a decoder for decoding images received from the // host. // // Sequence of actions with a decoder is as follows: // -// 1. BeginDecode(VideoFrame) +// 1. BeginDecode(PartialDecodeDone, DecodeDone, VideoFrame) // 2. PartialDecode(HostMessage) // ... // 3. EndDecode() @@ -34,27 +37,22 @@ namespace remoting { // decoder (most likely the renderer) and the decoder. class Decoder { public: - typedef std::vector<gfx::Rect> UpdatedRects; - typedef Callback2<scoped_refptr<media::VideoFrame>, UpdatedRects>::Type - PartialDecodeDoneCallback; - typedef Callback1<scoped_refptr<media::VideoFrame> >::Type - DecodeDoneCallback; - - Decoder(PartialDecodeDoneCallback* partial_decode_done_callback, - DecodeDoneCallback* decode_done_callback) - : partial_decode_done_(partial_decode_done_callback), - decode_done_(decode_done_callback) { - } virtual ~Decoder() { } // Tell the decoder to use |frame| as a target to write the decoded image // for the coming update stream. + // If decode is partially done and |frame| can be read, |partial_decode_done| + // is called and |update_rects| contains the updated regions. + // If decode is completed |decode_done| is called. // Return true if the decoder can writes output to |frame| and accept // the codec format. // TODO(hclam): Provide more information when calling this function. - virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame) = 0; + virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame, + UpdatedRects* updated_rects, + Task* partial_decode_done, + Task* decode_done) = 0; // Give a HostMessage that contains the update stream packet that contains // the encoded data to the decoder. @@ -62,7 +60,7 @@ class Decoder { // If the decoder has written something into |frame|, // |partial_decode_done_| is called with |frame| and updated regions. // Return true if the decoder can accept |message| and decode it. - virtual bool PartialDecode(chromotocol_pb::HostMessage* message) = 0; + virtual bool PartialDecode(HostMessage* message) = 0; // Notify the decoder that we have received the last update stream packet. // If the decoding of the update stream has completed |decode_done_| is @@ -70,21 +68,6 @@ class Decoder { // If the update stream is not received fully and this method is called the // decoder should also call |decode_done_| as soon as possible. virtual void EndDecode() = 0; - - protected: - PartialDecodeDoneCallback* partial_decode_done() { - return partial_decode_done_.get(); - } - - DecodeDoneCallback* decode_done() { - return decode_done_.get(); - } - - private: - scoped_ptr<PartialDecodeDoneCallback> partial_decode_done_; - scoped_ptr<DecodeDoneCallback> decode_done_; - - DISALLOW_COPY_AND_ASSIGN(Decoder); }; } // namespace remoting diff --git a/remoting/client/decoder_verbatim.cc b/remoting/client/decoder_verbatim.cc index 7cbf428..8fb54ac 100644 --- a/remoting/client/decoder_verbatim.cc +++ b/remoting/client/decoder_verbatim.cc @@ -6,62 +6,80 @@ namespace remoting { -bool DecoderVerbatim::BeginDecode(scoped_refptr<media::VideoFrame> frame) { - // TODO(hclam): Check if we can accept the codec. +bool DecoderVerbatim::BeginDecode(scoped_refptr<media::VideoFrame> frame, + UpdatedRects* updated_rects, + Task* partial_decode_done, + Task* decode_done) { + DCHECK(!partial_decode_done_.get()); + DCHECK(!decode_done_.get()); + DCHECK(!updated_rects_); + + partial_decode_done_.reset(partial_decode_done); + decode_done_.reset(decode_done); + updated_rects_ = updated_rects; + + // TODO(hclam): Check if we can accept the color format of the video frame and + // the codec. frame_ = frame; return true; } -bool DecoderVerbatim::PartialDecode(chromotocol_pb::HostMessage* message) { - scoped_ptr<chromotocol_pb::HostMessage> msg_deleter(message); +bool DecoderVerbatim::PartialDecode(HostMessage* message) { + scoped_ptr<HostMessage> msg_deleter(message); - // TODO(hclam): Support YUV. - if (static_cast<int>(message->update_stream_packet().header().pixel_format()) - != static_cast<int>(frame_->format())) { - return false; - } int width = message->update_stream_packet().header().width(); int height = message->update_stream_packet().header().height(); int x = message->update_stream_packet().header().x(); int y = message->update_stream_packet().header().y(); - chromotocol_pb::PixelFormat pixel_format = + PixelFormat pixel_format = message->update_stream_packet().header().pixel_format(); int bytes_per_pixel = 0; // TODO(hclam): Extract the following to an util function. - if (pixel_format == chromotocol_pb::PixelFormatRgb24) { + if (pixel_format == PixelFormatRgb24) { bytes_per_pixel = 3; - } else if (pixel_format == chromotocol_pb::PixelFormatRgb565) { + } else if (pixel_format == PixelFormatRgb565) { bytes_per_pixel = 2; - } else if (pixel_format == chromotocol_pb::PixelFormatRgb32) { + } else if (pixel_format == PixelFormatRgb32) { bytes_per_pixel = 4; - } else if (pixel_format != chromotocol_pb::PixelFormatAscii) { + } else if (pixel_format != PixelFormatAscii) { bytes_per_pixel = 1; } else { NOTREACHED() << "Pixel format not supported"; } + if (static_cast<PixelFormat>(frame_->format()) != pixel_format) { + NOTREACHED() << "Pixel format of message doesn't match the video frame. " + "Expected vs received = " + << frame_->format() << " vs " << pixel_format + << " Color space conversion required."; + } + // Copy the data line by line. const int src_stride = bytes_per_pixel * width; const char* src = message->update_stream_packet().data().c_str(); const int dest_stride = frame_->stride(media::VideoFrame::kRGBPlane); uint8* dest = frame_->data(media::VideoFrame::kRGBPlane) + - dest_stride * y + bytes_per_pixel * x; + dest_stride * y + bytes_per_pixel * x; for (int i = 0; i < height; ++i) { memcpy(dest, src, src_stride); dest += dest_stride; src += src_stride; } - UpdatedRects rects; - rects.push_back(gfx::Rect(x, y, width, height)); - partial_decode_done()->Run(frame_, rects); + updated_rects_->clear(); + updated_rects_->push_back(gfx::Rect(x, y, width, height)); + partial_decode_done_->Run(); return true; } void DecoderVerbatim::EndDecode() { - decode_done()->Run(frame_); + decode_done_->Run(); + + partial_decode_done_.reset(); + decode_done_.reset(); frame_ = NULL; + updated_rects_ = NULL; } } // namespace remoting diff --git a/remoting/client/decoder_verbatim.h b/remoting/client/decoder_verbatim.h index 94d2f81..5d18a48 100644 --- a/remoting/client/decoder_verbatim.h +++ b/remoting/client/decoder_verbatim.h @@ -11,19 +11,25 @@ namespace remoting { class DecoderVerbatim : public Decoder { public: - DecoderVerbatim(PartialDecodeDoneCallback* partial_decode_done_callback, - DecodeDoneCallback* decode_done_callback) - : Decoder(partial_decode_done_callback, decode_done_callback) { + DecoderVerbatim() { } // Decoder implementations. - virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame); - virtual bool PartialDecode(chromotocol_pb::HostMessage* message); + virtual bool BeginDecode(scoped_refptr<media::VideoFrame> frame, + UpdatedRects* update_rects, + Task* partial_decode_done, + Task* decode_done); + virtual bool PartialDecode(HostMessage* message); virtual void EndDecode(); private: + // Tasks to call when decode is done. + scoped_ptr<Task> partial_decode_done_; + scoped_ptr<Task> decode_done_; + // The video frame to write to. scoped_refptr<media::VideoFrame> frame_; + UpdatedRects* updated_rects_; DISALLOW_COPY_AND_ASSIGN(DecoderVerbatim); }; diff --git a/remoting/client/host_connection.cc b/remoting/client/host_connection.cc index 00c542e..cc73b73 100644 --- a/remoting/client/host_connection.cc +++ b/remoting/client/host_connection.cc @@ -26,22 +26,28 @@ void HostConnection::Connect(const std::string& username, } void HostConnection::Disconnect() { - if (jingle_channel_.get()) + // TODO(hclam): It's not thread safe to read the state. + if (jingle_channel_.get() && + jingle_channel_->state() != JingleChannel::CLOSED) { jingle_channel_->Close(); + } - if (jingle_client_.get()) + // TODO(hclam): It's not thread safe to read the state. + if (jingle_client_.get() && + jingle_client_->state() != JingleClient::CLOSED) { jingle_client_->Close(); + } } void HostConnection::OnStateChange(JingleChannel* channel, JingleChannel::State state) { DCHECK(handler_); - if (state == JingleChannel::FAILED) + if (state == JingleChannel::OPEN) + handler_->OnConnectionOpened(this); + else if (state == JingleChannel::FAILED) handler_->OnConnectionFailed(this); else if (state == JingleChannel::CLOSED) handler_->OnConnectionClosed(this); - else if (state == JingleChannel::OPEN) - handler_->OnConnectionOpened(this); } void HostConnection::OnPacketReceived(JingleChannel* channel, diff --git a/remoting/client/simple_client.cc b/remoting/client/simple_client.cc index da65b2a..474f02f 100644 --- a/remoting/client/simple_client.cc +++ b/remoting/client/simple_client.cc @@ -11,47 +11,20 @@ #include "base/at_exit.h" #include "base/message_loop.h" #include "base/stl_util-inl.h" -#include "media/base/data_buffer.h" #include "remoting/base/protocol_decoder.h" +#include "remoting/client/client_util.h" #include "remoting/client/host_connection.h" -#include "remoting/jingle_glue/jingle_channel.h" -#include "remoting/jingle_glue/jingle_client.h" - -using chromotocol_pb::HostMessage; -using chromotocol_pb::InitClientMessage; -using chromotocol_pb::BeginUpdateStreamMessage; -using chromotocol_pb::EndUpdateStreamMessage; -using chromotocol_pb::UpdateStreamPacketMessage; + +using remoting::BeginUpdateStreamMessage; +using remoting::EndUpdateStreamMessage; using remoting::HostConnection; +using remoting::HostMessage; using remoting::HostMessageList; +using remoting::InitClientMessage; using remoting::JingleClient; using remoting::JingleChannel; using remoting::ProtocolDecoder; - -void SetConsoleEcho(bool on) { -#ifdef WIN32 - HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); - if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL)) - return; - - DWORD mode; - if (!GetConsoleMode(hIn, &mode)) - return; - - if (on) { - mode = mode | ENABLE_ECHO_INPUT; - } else { - mode = mode & ~ENABLE_ECHO_INPUT; - } - - SetConsoleMode(hIn, mode); -#else - if (on) - system("stty echo"); - else - system("stty -echo"); -#endif -} +using remoting::UpdateStreamPacketMessage; class SimpleHostEventHandler : public HostConnection::EventHandler { public: @@ -137,50 +110,13 @@ class SimpleHostEventHandler : public HostConnection::EventHandler { int main(int argc, char** argv) { base::AtExitManager exit_manager; + std::string host_jid, username, auth_token; - if (argc > 2) { - std::cerr << "Usage: " << argv[0] << " [<host_jid>]" << std::endl; - return 1; - } - - // Get host JID from command line arguments, or stdin if not specified. - std::string host_jid; - if (argc == 2) { - host_jid = argv[1]; - } else { - std::cout << "Host JID: "; - std::cin >> host_jid; - std::cin.ignore(); // Consume the leftover '\n' - } - if (host_jid.find("/chromoting") == std::string::npos) { - std::cerr << "Error: Expected Host JID in format: <jid>/chromoting<id>" - << std::endl; + if (remoting::GetLoginInfo(host_jid, username, auth_token)) { + std::cerr << "Cannot get valid login info." << std::endl; return 1; } - // Get username (JID). - // Extract default JID from host_jid. - std::string username; - 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 = default_username; - } - if (username.length() == 0) { - std::cerr << "Error: Expected valid JID username" << std::endl; - return 1; - } - - // Get auth token. - std::string auth_token; - std::cout << "Auth Token: "; - getline(std::cin, auth_token); - // The message loop that everything runs on. MessageLoop main_loop; SimpleHostEventHandler handler(&main_loop); diff --git a/remoting/client/x11_client.cc b/remoting/client/x11_client.cc new file mode 100644 index 0000000..6b8e4e2 --- /dev/null +++ b/remoting/client/x11_client.cc @@ -0,0 +1,262 @@ +// 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. +// +// This file implements a X11 chromoting client. + +#include <iostream> + +#include "base/at_exit.h" +#include "base/message_loop.h" +#include "base/stl_util-inl.h" +#include "remoting/client/host_connection.h" +#include "remoting/client/client_util.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> + +namespace remoting { + +class X11Client : public base::RefCountedThreadSafe<X11Client>, + public HostConnection::EventHandler { + public: + X11Client(std::string host_jid, std::string username, std::string auth_token) + : display_(NULL), + window_(0), + width_(0), + height_(0), + host_jid_(host_jid), + username_(username), + auth_token_(auth_token) { + } + + virtual ~X11Client() { + DCHECK(!display_); + DCHECK(!window_); + } + + // Starts the remoting client and the message loop. Returns only after + // the message loop has terminated. + void Run() { + message_loop_.PostTask(FROM_HERE, + NewRunnableMethod(this, &X11Client::DoInitX11)); + message_loop_.PostTask(FROM_HERE, + NewRunnableMethod(this, &X11Client::DoInitConnection)); + message_loop_.Run(); + } + + //////////////////////////////////////////////////////////////////////////// + // 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 establised." << std::endl; + } + + virtual void OnConnectionClosed(HostConnection* conn) { + std::cout << "Connection closed." << std::endl; + Exit(); + } + + virtual void OnConnectionFailed(HostConnection* conn) { + std::cout << "Conection failed." << std::endl; + Exit(); + } + + private: + void DoInitX11() { + display_ = XOpenDisplay(NULL); + if (!display_) { + std::cout << "Error - cannot open display" << std::endl; + Exit(); + } + + // 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 DoInitConnection() { + // If the initialization of X11 has failed then return directly. + if (!display_) + return; + + // Creates a HostConnection object and connection to the host. + LOG(INFO) << "Connecting..."; + connection_.reset(new HostConnection(new ProtocolDecoder(), this)); + connection_->Connect(username_, auth_token_, host_jid_); + } + + void DoDestroyX11() { + if (display_ && window_) { + // Shutdown the window system. + XDestroyWindow(display_, window_); + XCloseDisplay(display_); + display_ = NULL; + window_ = 0; + } + } + + void DoDisconnect() { + if (connection_.get()) + connection_->Disconnect(); + } + + void Exit() { + // Disconnect the jingle channel and client. + message_loop_.PostTask(FROM_HERE, + NewRunnableMethod(this, &X11Client::DoDisconnect)); + + // Post a task to shutdown X11. + message_loop_.PostTask(FROM_HERE, + NewRunnableMethod(this, &X11Client::DoDestroyX11)); + + // Quit the current message loop. + message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + // 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 receievd: " << width_ << "x" << height_; + XResizeWindow(display_, window_, width_, height_); + + // Now construct the X11View that renders the remote desktop. + view_ = 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); + } + + // Members used for display. + Display* display_; + Window window_; + + // Dimension of the window. They are initialized when InitClient message is + // received. + int width_; + int height_; + + std::string host_jid_; + std::string username_; + std::string auth_token_; + scoped_ptr<HostConnection> connection_; + scoped_refptr<ChromotingView> view_; + + MessageLoop message_loop_; + + DISALLOW_COPY_AND_ASSIGN(X11Client); +}; + +} // namespace remoting + +int main() { + base::AtExitManager at_exit; + std::string host_jid, username, auth_token; + + if (!remoting::GetLoginInfo(host_jid, username, auth_token)) { + std::cout << "Cannot obtain login info" << std::endl; + return 1; + } + + scoped_refptr<remoting::X11Client> client = + new remoting::X11Client(host_jid, username, auth_token); + client->Run(); +} diff --git a/remoting/client/x11_view.cc b/remoting/client/x11_view.cc new file mode 100644 index 0000000..bdb54d4 --- /dev/null +++ b/remoting/client/x11_view.cc @@ -0,0 +1,151 @@ +// 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_view.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/Xcomposite.h> +#include "remoting/client/decoder_verbatim.h" + +namespace remoting { + +X11View::X11View(Display* display, int window, int width, int height) + : display_(display), + window_(window), + picture_(0), + width_(width), + height_(height) { +} + +X11View::~X11View() { +} + +void X11View::Paint() { + // TODO(hclam): Paint only the updated regions. + all_update_rects_.clear(); + + // If we have not initialized the render target then do it now. + if (!frame_) + InitPaintTarget(); + + // Upload the image to a pixmap. And then creats a picture from the pixmap + // and composite the picture over the picture represending the window. + + // Creates a XImage. + XImage image; + memset(&image, 0, sizeof(image)); + image.width = width_; + image.height = height_; + image.depth = 32; + image.bits_per_pixel = 32; + image.format = ZPixmap; + image.byte_order = LSBFirst; + image.bitmap_unit = 8; + image.bitmap_bit_order = LSBFirst; + image.bytes_per_line = frame_->stride(media::VideoFrame::kRGBPlane); + image.red_mask = 0xff; + image.green_mask = 0xff00; + image.blue_mask = 0xff0000; + image.data = reinterpret_cast<char*>( + frame_->data(media::VideoFrame::kRGBPlane)); + + // Creates a pixmap and uploads from the XImage. + unsigned long pixmap = XCreatePixmap(display_, window_, width_, height_, 32); + + GC gc = XCreateGC(display_, pixmap, 0, NULL); + XPutImage(display_, pixmap, gc, &image, 0, 0, 0, 0, width_, height_); + XFreeGC(display_, gc); + + // Creates the picture representing the pixmap. + unsigned long picture = XRenderCreatePicture( + display_, pixmap, + XRenderFindStandardFormat(display_, PictStandardARGB32), + 0, NULL); + + // Composite the picture over the picture representing the window. + XRenderComposite(display_, PictOpSrc, picture, 0, + picture_, 0, 0, 0, 0, 0, 0, + width_, height_); + + XRenderFreePicture(display_, picture); + XFreePixmap(display_, pixmap); +} + +void X11View::InitPaintTarget() { + // Testing XRender support. + int dummy; + bool xrender_support = XRenderQueryExtension(display_, &dummy, &dummy); + CHECK(xrender_support) << "XRender is not supported!"; + + XWindowAttributes attr; + XGetWindowAttributes(display_, window_, &attr); + + XRenderPictFormat* pictformat = XRenderFindVisualFormat( + display_, + attr.visual); + CHECK(pictformat) << "XRENDER does not support default visual"; + + picture_ = XRenderCreatePicture(display_, window_, pictformat, 0, NULL); + CHECK(picture_) << "Backing picture not created"; + + // Create the video frame to carry the decoded image. + media::VideoFrame::CreateFrame(media::VideoFrame::RGB32, width_, height_, + base::TimeDelta(), base::TimeDelta(), &frame_); + DCHECK(frame_); +} + +void X11View::HandleBeginUpdateStream(HostMessage* msg) { + scoped_ptr<HostMessage> deleter(msg); + + // TODO(hclam): Use the information from the message to create the decoder. + // We lazily construct the decoder. + if (!decoder_.get()) { + decoder_.reset(new DecoderVerbatim()); + } + + // Tell the decoder to do start decoding. + decoder_->BeginDecode(frame_, &update_rects_, + NewRunnableMethod(this, &X11View::OnPartialDecodeDone), + NewRunnableMethod(this, &X11View::OnDecodeDone)); +} + +void X11View::HandleUpdateStreamPacket(HostMessage* msg) { + decoder_->PartialDecode(msg); +} + +void X11View::HandleEndUpdateStream(HostMessage* msg) { + scoped_ptr<HostMessage> deleter(msg); + decoder_->EndDecode(); +} + +void X11View::OnPartialDecodeDone() { + // Decoder has produced some output so schedule a paint. We'll get a Paint() + // call in the short future. Note that we can get UpdateStreamPacket during + // this short period of time and we will perform decode again and the + // information of updated rects will be lost. + // There are several ways to solve this problem. + // 1. Merge the updated rects and perform one paint. + // 2. Queue the updated rects and perform two paints. + // 3. Ignore the updated rects and always paint the full image. Since we + // use one frame as output this will always be correct. + // We will take (1) and simply concat the list of rectangles. + all_update_rects_.insert(all_update_rects_.begin() + + all_update_rects_.size(), + update_rects_.begin(), update_rects_.end()); + + // TODO(hclam): Make sure we call this method on the right thread. Since + // decoder is single-threaded we don't have a problem but we better post + // a task to do the right thing. + XEvent event; + event.type = Expose; + XSendEvent(display_, static_cast<int>(window_), true, ExposureMask, &event); +} + +void X11View::OnDecodeDone() { + // Since we do synchronous decoding here there's nothing in this method. +} + +} // namespace remoting diff --git a/remoting/client/x11_view.h b/remoting/client/x11_view.h new file mode 100644 index 0000000..1959e18 --- /dev/null +++ b/remoting/client/x11_view.h @@ -0,0 +1,56 @@ +// 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_VIEW_H_ +#define REMOTING_CLIENT_X11_VIEW_H_ + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "media/base/video_frame.h" +#include "remoting/client/decoder.h" +#include "remoting/client/chromoting_view.h" + +typedef struct _XDisplay Display; + +namespace remoting { + +// A ChromotingView implemented using X11 and XRender. +class X11View : public ChromotingView { + public: + X11View(Display* display, int window, int width, int height); + + virtual ~X11View(); + + // ChromotingView implementations. + virtual void Paint(); + virtual void HandleBeginUpdateStream(HostMessage* msg); + virtual void HandleUpdateStreamPacket(HostMessage* msg); + virtual void HandleEndUpdateStream(HostMessage* msg) ; + + private: + void InitPaintTarget(); + void OnPartialDecodeDone(); + void OnDecodeDone(); + + Display* display_; + int window_; + int width_; + int height_; + + // A picture created in the X server that represents drawing area of the + // window. + int picture_; + + scoped_refptr<media::VideoFrame> frame_; + UpdatedRects update_rects_; + UpdatedRects all_update_rects_; + + scoped_ptr<Decoder> decoder_; + + DISALLOW_COPY_AND_ASSIGN(X11View); +}; + +} // namespace remoting + +#endif // REMOTING_CLIENT_X11_VIEW_H_ |