summaryrefslogtreecommitdiffstats
path: root/remoting/client
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/client')
-rw-r--r--remoting/client/chromoting_view.h37
-rw-r--r--remoting/client/client_util.cc76
-rw-r--r--remoting/client/client_util.h19
-rw-r--r--remoting/client/decoder.h43
-rw-r--r--remoting/client/decoder_verbatim.cc56
-rw-r--r--remoting/client/decoder_verbatim.h16
-rw-r--r--remoting/client/host_connection.cc16
-rw-r--r--remoting/client/simple_client.cc84
-rw-r--r--remoting/client/x11_client.cc262
-rw-r--r--remoting/client/x11_view.cc151
-rw-r--r--remoting/client/x11_view.h56
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_