diff options
author | jamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-31 20:10:36 +0000 |
---|---|---|
committer | jamiewalch@google.com <jamiewalch@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-31 20:10:36 +0000 |
commit | 135d0a4e737b0f15836a694900994b670ee1a20b (patch) | |
tree | b6df0139b9b5cd12cdbfa31b9061bf9e0dadac2f /remoting/host | |
parent | 3e1a05ab9d6496e84879518d974320fbc561465c (diff) | |
download | chromium_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.cc | 78 | ||||
-rw-r--r-- | remoting/host/chromoting_host.cc | 7 | ||||
-rw-r--r-- | remoting/host/chromoting_host.h | 6 | ||||
-rw-r--r-- | remoting/host/chromoting_host_unittest.cc | 34 | ||||
-rw-r--r-- | remoting/host/curtain_mac.cc | 25 | ||||
-rw-r--r-- | remoting/host/event_executor_mac.cc | 33 | ||||
-rw-r--r-- | remoting/host/host_plugin.cc | 4 | ||||
-rw-r--r-- | remoting/host/simple_host_process.cc | 2 |
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()); |