summaryrefslogtreecommitdiffstats
path: root/remoting/host
diff options
context:
space:
mode:
authorjamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-31 20:10:36 +0000
committerjamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-31 20:10:36 +0000
commit135d0a4e737b0f15836a694900994b670ee1a20b (patch)
treeb6df0139b9b5cd12cdbfa31b9061bf9e0dadac2f /remoting/host
parent3e1a05ab9d6496e84879518d974320fbc561465c (diff)
downloadchromium_src-135d0a4e737b0f15836a694900994b670ee1a20b.zip
chromium_src-135d0a4e737b0f15836a694900994b670ee1a20b.tar.gz
chromium_src-135d0a4e737b0f15836a694900994b670ee1a20b.tar.bz2
Initial curtain mode implementation.
BUG= TEST= Review URL: http://codereview.chromium.org/6849027 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@87350 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/host')
-rw-r--r--remoting/host/capturer_mac.cc78
-rw-r--r--remoting/host/chromoting_host.cc7
-rw-r--r--remoting/host/chromoting_host.h6
-rw-r--r--remoting/host/chromoting_host_unittest.cc34
-rw-r--r--remoting/host/curtain_mac.cc25
-rw-r--r--remoting/host/event_executor_mac.cc33
-rw-r--r--remoting/host/host_plugin.cc4
-rw-r--r--remoting/host/simple_host_process.cc2
8 files changed, 135 insertions, 54 deletions
diff --git a/remoting/host/capturer_mac.cc b/remoting/host/capturer_mac.cc
index bd5f41d..c507858 100644
--- a/remoting/host/capturer_mac.cc
+++ b/remoting/host/capturer_mac.cc
@@ -133,8 +133,9 @@ class CapturerMac : public Capturer {
virtual const gfx::Size& size_most_recent() const OVERRIDE;
private:
- void FastBlit(const VideoFrameBuffer& buffer);
- void SlowBlit(const VideoFrameBuffer& buffer);
+ void GlBlitFast(const VideoFrameBuffer& buffer);
+ void GlBlitSlow(const VideoFrameBuffer& buffer);
+ void CgBlit(const VideoFrameBuffer& buffer, const InvalidRects& rects);
void CaptureRects(const InvalidRects& rects,
CaptureCompletedCallback* callback);
@@ -166,6 +167,10 @@ class CapturerMac : public Capturer {
// The current buffer with valid data for reading.
int current_buffer_;
+ // The last buffer into which we captured, or NULL for the first capture for
+ // a particular screen resolution.
+ uint8* last_buffer_;
+
// Format of pixels returned in buffer.
media::VideoFrame::Format pixel_format_;
@@ -177,6 +182,7 @@ class CapturerMac : public Capturer {
CapturerMac::CapturerMac()
: cgl_context_(NULL),
current_buffer_(0),
+ last_buffer_(NULL),
pixel_format_(media::VideoFrame::RGB32),
capturing_(true) {
// TODO(dmaclach): move this initialization out into session_manager,
@@ -224,7 +230,18 @@ void CapturerMac::ScreenConfigurationChanged() {
ReleaseBuffers();
InvalidRects rects;
helper_.SwapInvalidRects(rects);
+ last_buffer_ = NULL;
+
CGDirectDisplayID mainDevice = CGMainDisplayID();
+ int width = CGDisplayPixelsWide(mainDevice);
+ int height = CGDisplayPixelsHigh(mainDevice);
+ InvalidateScreen(gfx::Size(width, height));
+
+ if (CGDisplayIsBuiltin(mainDevice)) {
+ VLOG(3) << "OpenGL support not available.";
+ return;
+ }
+
CGLPixelFormatAttribute attributes[] = {
kCGLPFAFullScreen,
kCGLPFADisplayMask,
@@ -243,9 +260,6 @@ void CapturerMac::ScreenConfigurationChanged() {
CGLSetFullScreen(cgl_context_);
CGLSetCurrentContext(cgl_context_);
- int width = CGDisplayPixelsWide(mainDevice);
- int height = CGDisplayPixelsHigh(mainDevice);
- InvalidateScreen(gfx::Size(width, height));
size_t buffer_size = width * height * sizeof(uint32_t);
pixel_buffer_object_.Init(cgl_context_, buffer_size);
}
@@ -278,16 +292,26 @@ void CapturerMac::CaptureInvalidRects(CaptureCompletedCallback* callback) {
VideoFrameBuffer& current_buffer = buffers_[current_buffer_];
current_buffer.Update();
- if (pixel_buffer_object_.get() != 0) {
- FastBlit(current_buffer);
+ bool flip = true; // GL capturers need flipping.
+ if (cgl_context_) {
+ if (pixel_buffer_object_.get() != 0) {
+ GlBlitFast(current_buffer);
+ } else {
+ GlBlitSlow(current_buffer);
+ }
} else {
- SlowBlit(current_buffer);
+ CgBlit(current_buffer, rects);
+ flip = false;
}
DataPlanes planes;
- planes.data[0] = current_buffer.ptr() +
- (current_buffer.size().height() - 1) * current_buffer.bytes_per_row();
- planes.strides[0] = -current_buffer.bytes_per_row();
+ planes.data[0] = current_buffer.ptr();
+ planes.strides[0] = current_buffer.bytes_per_row();
+ if (flip) {
+ planes.strides[0] = -planes.strides[0];
+ planes.data[0] +=
+ (current_buffer.size().height() - 1) * current_buffer.bytes_per_row();
+ }
data = new CaptureData(planes, gfx::Size(current_buffer.size()),
pixel_format());
@@ -301,7 +325,7 @@ void CapturerMac::CaptureInvalidRects(CaptureCompletedCallback* callback) {
delete callback;
}
-void CapturerMac::FastBlit(const VideoFrameBuffer& buffer) {
+void CapturerMac::GlBlitFast(const VideoFrameBuffer& buffer) {
CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pixel_buffer_object_.get());
glReadPixels(0, 0, buffer.size().width(), buffer.size().height(),
@@ -319,14 +343,14 @@ void CapturerMac::FastBlit(const VideoFrameBuffer& buffer) {
// If glUnmapBuffer returns false, then the contents of the data store are
// undefined. This might be because the screen mode has changed, in which
// case it will be recreated in ScreenConfigurationChanged, but releasing
- // the object here is the best option. Capturing will fall back on SlowBlit
- // until such time as the pixel buffer object is recreated.
+ // the object here is the best option. Capturing will fall back on
+ // GlBlitSlow until such time as the pixel buffer object is recreated.
pixel_buffer_object_.Release();
}
glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
}
-void CapturerMac::SlowBlit(const VideoFrameBuffer& buffer) {
+void CapturerMac::GlBlitSlow(const VideoFrameBuffer& buffer) {
CGLContextObj CGL_MACRO_CONTEXT = cgl_context_;
glReadBuffer(GL_FRONT);
glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
@@ -340,6 +364,30 @@ void CapturerMac::SlowBlit(const VideoFrameBuffer& buffer) {
glPopClientAttrib();
}
+void CapturerMac::CgBlit(const VideoFrameBuffer& buffer,
+ const InvalidRects& rects) {
+ if (last_buffer_)
+ memcpy(buffer.ptr(), last_buffer_,
+ buffer.bytes_per_row() * buffer.size().height());
+ last_buffer_ = buffer.ptr();
+ CGDirectDisplayID main_display = CGMainDisplayID();
+ uint8* display_base_address =
+ reinterpret_cast<uint8*>(CGDisplayBaseAddress(main_display));
+ int src_bytes_per_row = CGDisplayBytesPerRow(main_display);
+ int src_bytes_per_pixel = CGDisplayBitsPerPixel(main_display) / 8;
+ for (InvalidRects::iterator i = rects.begin(); i != rects.end(); ++i) {
+ int src_row_offset = i->x() * src_bytes_per_pixel;
+ int dst_row_offset = i->x() * sizeof(uint32_t);
+ int rect_width_in_bytes = i->width() * src_bytes_per_pixel;
+ int ymax = i->height() + i->y();
+ for (int y = i->y(); y < ymax; ++y) {
+ memcpy(buffer.ptr() + y * buffer.bytes_per_row() + dst_row_offset,
+ display_base_address + y * src_bytes_per_row + src_row_offset,
+ rect_width_in_bytes);
+ }
+ }
+}
+
const gfx::Size& CapturerMac::size_most_recent() const {
return helper_.size_most_recent();
}
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index 7aff37b..5227e34 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -63,7 +63,7 @@ ChromotingHost::ChromotingHost(ChromotingHostContext* context,
state_(kInitial),
protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
is_curtained_(false),
- preauthenticated_(false) {
+ is_me2mom_(false) {
DCHECK(desktop_environment_.get());
}
@@ -180,7 +180,8 @@ void ChromotingHost::AddStatusObserver(
void ChromotingHost::OnConnectionOpened(ConnectionToClient* connection) {
DCHECK_EQ(context_->network_message_loop(), MessageLoop::current());
VLOG(1) << "Connection to client established.";
- if (preauthenticated_) {
+ if (is_me2mom_) {
+ // TODO(wez): Improve our authentication framework.
context_->main_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &ChromotingHost::ProcessPreAuthentication,
@@ -406,7 +407,7 @@ bool ChromotingHost::HasAuthenticatedClients() const {
void ChromotingHost::EnableCurtainMode(bool enable) {
// TODO(jamiewalch): This will need to be more sophisticated when we think
// about proper crash recovery and daemon mode.
- if (enable == is_curtained_)
+ if (is_me2mom_ || enable == is_curtained_)
return;
desktop_environment_->curtain()->EnableCurtainMode(enable);
is_curtained_ = enable;
diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h
index 1522aad..20d476a 100644
--- a/remoting/host/chromoting_host.h
+++ b/remoting/host/chromoting_host.h
@@ -123,8 +123,8 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
// |config| is transferred to the object. Must be called before Start().
void set_protocol_config(protocol::CandidateSessionConfig* config);
- void set_preauthenticated(bool preauthenticated) {
- preauthenticated_ = preauthenticated;
+ void set_me2mom(bool is_me2mom) {
+ is_me2mom_ = is_me2mom;
}
private:
@@ -211,7 +211,7 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>,
// Whether or not the host is running in "Me2Mom" mode, in which connections
// are pre-authenticated, and hence the local login challenge can be bypassed.
- bool preauthenticated_;
+ bool is_me2mom_;
DISALLOW_COPY_AND_ASSIGN(ChromotingHost);
};
diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc
index 9da2a55..d664805 100644
--- a/remoting/host/chromoting_host_unittest.cc
+++ b/remoting/host/chromoting_host_unittest.cc
@@ -404,4 +404,38 @@ TEST_F(ChromotingHostTest, CurtainModeFailSecond) {
message_loop_.Run();
}
+ACTION_P(SetBool, var) { *var = true; }
+
+TEST_F(ChromotingHostTest, CurtainModeMe2Mom) {
+ host_->Start(NewRunnableFunction(&PostQuitTask, &message_loop_));
+ host_->set_me2mom(true);
+
+ EXPECT_CALL(client_stub_, BeginSessionResponse(_, _))
+ .WillOnce(RunDoneTask());
+
+ // When the video packet is received we first shutdown ChromotingHost
+ // then execute the done task.
+ bool curtain_activated = false;
+ {
+ InSequence s;
+ // Can't just expect Times(0) because if it fails then the host will
+ // not be shut down and the message loop will never exit.
+ EXPECT_CALL(*curtain_, EnableCurtainMode(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(SetBool(&curtain_activated));
+ EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
+ .WillOnce(DoAll(
+ InvokeWithoutArgs(host_.get(), &ChromotingHost::Shutdown),
+ RunDoneTask()))
+ .RetiresOnSaturation();
+ EXPECT_CALL(video_stub_, ProcessVideoPacket(_, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(*connection_.get(), Disconnect())
+ .RetiresOnSaturation();
+ }
+ SimulateClientConnection(0, true);
+ message_loop_.Run();
+ host_->set_me2mom(false);
+ EXPECT_THAT(curtain_activated, false);
+}
} // namespace remoting
diff --git a/remoting/host/curtain_mac.cc b/remoting/host/curtain_mac.cc
index b6c741a..aef87c1 100644
--- a/remoting/host/curtain_mac.cc
+++ b/remoting/host/curtain_mac.cc
@@ -8,6 +8,12 @@
#include "base/compiler_specific.h"
#include "base/logging.h"
+namespace {
+static const char* kCGSessionPath =
+ "/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/"
+ "CGSession";
+}
+
namespace remoting {
namespace {
@@ -22,12 +28,29 @@ class CurtainMac : public Curtain {
};
void CurtainMac::EnableCurtainMode(bool enable) {
- NOTIMPLEMENTED();
+ // Whether curtain mode is being enabled or disabled, switch out the session.
+ // TODO(jamiewalch): If curtain mode is being enabled at the login screen, it
+ // should be deferred until the user logs in.
+ pid_t child = fork();
+ if (child == 0) {
+ execl(kCGSessionPath, kCGSessionPath, "-suspend", (char*)0);
+ exit(1);
+ } else if (child > 0) {
+ int status = 0;
+ waitpid(child, &status, 0);
+ // To ensure that the system has plenty of time to notify the CGDisplay-
+ // ReconfigurationCallback, sleep here. 1s is probably overkill.
+ sleep(1);
+ }
}
} // namespace
Curtain* Curtain::Create() {
+ // There's no need to check for curtain mode being enabled here because on
+ // the mac it's easy for a local user to recover if anything crashes while
+ // a session is active--they just have to enter a password to switch their
+ // session back in.
return new CurtainMac();
}
diff --git a/remoting/host/event_executor_mac.cc b/remoting/host/event_executor_mac.cc
index 4c65c28..be782a2 100644
--- a/remoting/host/event_executor_mac.cc
+++ b/remoting/host/event_executor_mac.cc
@@ -36,7 +36,7 @@ class EventExecutorMac : public EventExecutor {
MessageLoop* message_loop_;
Capturer* capturer_;
int last_x_, last_y_;
- int modifiers_, mouse_buttons_;
+ int mouse_buttons_;
DISALLOW_COPY_AND_ASSIGN(EventExecutorMac);
};
@@ -44,8 +44,7 @@ class EventExecutorMac : public EventExecutor {
EventExecutorMac::EventExecutorMac(
MessageLoop* message_loop, Capturer* capturer)
: message_loop_(message_loop),
- capturer_(capturer), last_x_(0), last_y_(0), modifiers_(0),
- mouse_buttons_(0) {
+ capturer_(capturer), last_x_(0), last_y_(0), mouse_buttons_(0) {
}
// Hard-coded mapping from Virtual Key codes to Mac KeySyms.
@@ -220,31 +219,9 @@ void EventExecutorMac::InjectKeyEvent(const KeyEvent* event, Task* done) {
if (key_code >= 0 && key_code < 256) {
int key_sym = kUsVkeyToKeysym[key_code];
if (key_sym != -1) {
- base::mac::ScopedCFTypeRef<CGEventRef> kbd_event(
- CGEventCreateKeyboardEvent(0, kUsVkeyToKeysym[key_code],
- event->pressed()));
- int this_modifier = 0;
- switch (key_sym) {
- case kVK_Shift: case kVK_RightShift:
- this_modifier = kCGEventFlagMaskShift;
- break;
- case kVK_Control: case kVK_RightControl:
- this_modifier = kCGEventFlagMaskControl;
- break;
- case kVK_Command:
- this_modifier = kCGEventFlagMaskCommand;
- break;
- case kVK_Option: case kVK_RightOption:
- this_modifier = kCGEventFlagMaskAlternate;
- break;
- }
- if (this_modifier && event->pressed()) {
- modifiers_ |= this_modifier;
- } else if (this_modifier && !event->pressed()) {
- modifiers_ &= ~this_modifier;
- }
- CGEventSetFlags(kbd_event, modifiers_);
- CGEventPost(kCGSessionEventTap, kbd_event);
+ // We use the deprecated event injection API because the new one doesn't
+ // work with switched-out sessions (curtain mode).
+ CGPostKeyboardEvent(0, key_sym, event->pressed());
}
}
}
diff --git a/remoting/host/host_plugin.cc b/remoting/host/host_plugin.cc
index d152ce2..11069d4 100644
--- a/remoting/host/host_plugin.cc
+++ b/remoting/host/host_plugin.cc
@@ -462,9 +462,7 @@ bool HostNPScriptObject::Connect(const NPVariant* args,
remoting::ChromotingHost::Create(&host_context_, host_config,
access_verifier.release());
host->AddStatusObserver(register_request);
-
- // TODO(wez): Improve our authentication framework.
- host->set_preauthenticated(true);
+ host->set_me2mom(true);
// Nothing went wrong, so lets save the host, config and request.
host_ = host;
diff --git a/remoting/host/simple_host_process.cc b/remoting/host/simple_host_process.cc
index 7bdb7fb..286ec07 100644
--- a/remoting/host/simple_host_process.cc
+++ b/remoting/host/simple_host_process.cc
@@ -176,7 +176,7 @@ class SimpleHost {
host = ChromotingHost::Create(&context, config,
access_verifier.release());
}
- host->set_preauthenticated(is_me2mom_);
+ host->set_me2mom(is_me2mom_);
if (protocol_config_.get()) {
host->set_protocol_config(protocol_config_.release());