Implement single monitor capture on Mac.

BUG=2787, 2824
TESTED=MacBook Pro Retina with an external monitor; verified changing display configuration while capturing; add/remove monitor while capturing; verified cursor position.
R=sergeyu@chromium.org

Review URL: https://webrtc-codereview.appspot.com/7479004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5471 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
jiayl@webrtc.org 2014-02-01 02:03:24 +00:00
parent 83aee8f450
commit 1af5ea0538
8 changed files with 284 additions and 107 deletions

View File

@ -38,6 +38,8 @@
"mac/desktop_configuration.mm",
"mac/desktop_configuration_monitor.h",
"mac/desktop_configuration_monitor.cc",
"mac/osx_version.h",
"mac/osx_version.cc",
"mac/scoped_pixel_buffer_object.cc",
"mac/scoped_pixel_buffer_object.h",
"mouse_cursor.cc",

View File

@ -26,7 +26,11 @@ typedef intptr_t WindowId;
const WindowId kNullWindowId = 0;
typedef int ScreenId;
// Type used to identify screens on the desktop. Values are platform-specific:
// - On Windows: integer display device index.
// - On OSX: CGDirectDisplayID cast to intptr_t.
// - On Linux (with X11): TBD.
typedef intptr_t ScreenId;
// The screen id corresponds to all screen combined together.
const ScreenId kFullDesktopScreenId = -1;

View File

@ -55,6 +55,10 @@ struct MacDesktopConfiguration {
// Returns true if the given desktop configuration equals this one.
bool Equals(const MacDesktopConfiguration& other);
// Returns the pointer to the display configuration with the specified id.
const MacDisplayConfiguration* FindDisplayConfigurationById(
CGDirectDisplayID id);
// Bounds of the desktop in Density-Independent Pixels (DIPs).
DesktopRect bounds;

View File

@ -110,15 +110,8 @@ MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
MacDisplayConfiguration display_config =
GetConfigurationForScreen([screens objectAtIndex: i]);
// Handling mixed-DPI is hard, so we only return displays that match the
// "primary" display's DPI. The primary display is always the first in the
// list returned by [NSScreen screens].
if (i == 0) {
if (i == 0)
desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale;
} else if (desktop_config.dip_to_pixel_scale !=
display_config.dip_to_pixel_scale) {
continue;
}
// Cocoa uses bottom-up coordinates, so if the caller wants top-down then
// we need to invert the positions of secondary monitors relative to the
@ -126,8 +119,16 @@ MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
if (i > 0 && origin == TopLeftOrigin) {
InvertRectYOrigin(desktop_config.displays[0].bounds,
&display_config.bounds);
InvertRectYOrigin(desktop_config.displays[0].pixel_bounds,
&display_config.pixel_bounds);
// |display_bounds| is density dependent, so we need to convert the
// primay monitor's position into the secondary monitor's density context.
float scaling_factor = display_config.dip_to_pixel_scale /
desktop_config.displays[0].dip_to_pixel_scale;
DesktopRect primary_bounds = DesktopRect::MakeLTRB(
desktop_config.displays[0].pixel_bounds.left() * scaling_factor,
desktop_config.displays[0].pixel_bounds.top() * scaling_factor,
desktop_config.displays[0].pixel_bounds.right() * scaling_factor,
desktop_config.displays[0].pixel_bounds.bottom() * scaling_factor);
InvertRectYOrigin(primary_bounds, &display_config.pixel_bounds);
}
// Add the display to the configuration.
@ -143,7 +144,6 @@ MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
return desktop_config;
}
// For convenience of comparing MacDisplayConfigurations in
// MacDesktopConfiguration::Equals.
bool operator==(const MacDisplayConfiguration& left,
@ -161,4 +161,16 @@ bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) {
displays == other.displays;
}
// Finds the display configuration with the specified id.
const MacDisplayConfiguration*
MacDesktopConfiguration::FindDisplayConfigurationById(
CGDirectDisplayID id) {
for (MacDisplayConfigurations::const_iterator it = displays.begin();
it != displays.end(); ++it) {
if (it->id == id)
return &(*it);
}
return NULL;
}
} // namespace webrtc

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <sys/utsname.h>
#include "webrtc/system_wrappers/interface/logging.h"
namespace webrtc {
namespace {
int GetDarwinVersion() {
struct utsname uname_info;
if (uname(&uname_info) != 0) {
LOG(LS_ERROR) << "uname failed";
return 0;
}
if (strcmp(uname_info.sysname, "Darwin") != 0)
return 0;
char* dot;
int result = strtol(uname_info.release, &dot, 10);
if (*dot != '.') {
LOG(LS_ERROR) << "Failed to parse version";
return 0;
}
return result;
}
} // namespace
bool IsOSLionOrLater() {
static int darwin_version = GetDarwinVersion();
// Verify that the version has been parsed correctly.
if (darwin_version < 6) {
LOG_F(LS_ERROR) << "Invalid Darwin version: " << darwin_version;
abort();
}
// Darwin major version 11 corresponds to OSX 10.7.
return darwin_version >= 11;
}
} // namespace webrtc

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
namespace webrtc {
// Returns true if the OS version >= OSX 10.7.
bool IsOSLionOrLater();
} // namespace webrtc

View File

@ -17,8 +17,11 @@
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/osx_version.h"
#include "webrtc/modules/desktop_capture/mouse_cursor.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/system_wrappers/interface/scoped_refptr.h"
@ -27,31 +30,44 @@ namespace webrtc {
class MouseCursorMonitorMac : public MouseCursorMonitor {
public:
MouseCursorMonitorMac(const DesktopCaptureOptions& options,
CGWindowID window_id);
CGWindowID window_id,
ScreenId screen_id);
virtual ~MouseCursorMonitorMac();
virtual void Init(Callback* callback, Mode mode) OVERRIDE;
virtual void Capture() OVERRIDE;
private:
static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *user_parameter);
void DisplaysReconfigured(CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags);
void CaptureImage();
scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
CGWindowID window_id_;
ScreenId screen_id_;
Callback* callback_;
Mode mode_;
scoped_ptr<MouseCursor> last_cursor_;
};
MouseCursorMonitorMac::MouseCursorMonitorMac(
const DesktopCaptureOptions& options,
CGWindowID window_id)
CGWindowID window_id,
ScreenId screen_id)
: configuration_monitor_(options.configuration_monitor()),
window_id_(window_id),
screen_id_(screen_id),
callback_(NULL),
mode_(SHAPE_AND_POSITION) {
assert(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
if (screen_id != kInvalidScreenId && !IsOSLionOrLater()) {
// Single screen capture is not supported on pre OS X 10.7.
screen_id_ = kFullDesktopScreenId;
}
}
MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
@ -80,6 +96,21 @@ void MouseCursorMonitorMac::Capture() {
DesktopVector position(gc_position.x, gc_position.y);
configuration_monitor_->Lock();
MacDesktopConfiguration configuration =
configuration_monitor_->desktop_configuration();
configuration_monitor_->Unlock();
float scale = 1.0f;
// Find the dpi to physical pixel scale for the screen where the mouse cursor
// is.
for (MacDisplayConfigurations::iterator it = configuration.displays.begin();
it != configuration.displays.end(); ++it) {
if (it->bounds.Contains(position)) {
scale = it->dip_to_pixel_scale;
break;
}
}
// If we are capturing cursor for a specific window then we need to figure out
// if the current mouse position is covered by another window and also adjust
// |position| to make it relative to the window origin.
@ -142,10 +173,8 @@ void MouseCursorMonitorMac::Capture() {
}
}
}
CFRelease(window_array);
}
if (!found_window) {
// If we failed to get list of windows or the window wasn't in the list
// pretend that the cursor is outside the window. This can happen, e.g. if
@ -153,17 +182,32 @@ void MouseCursorMonitorMac::Capture() {
state = OUTSIDE;
position.set(-1, -1);
}
} else {
assert(screen_id_ >= kFullDesktopScreenId);
if (screen_id_ != kFullDesktopScreenId) {
// For single screen capturing, convert the position to relative to the
// target screen.
const MacDisplayConfiguration* config =
configuration.FindDisplayConfigurationById(
static_cast<CGDirectDisplayID>(screen_id_));
if (config) {
if (!config->pixel_bounds.Contains(position))
state = OUTSIDE;
position = position.subtract(config->bounds.top_left());
} else {
// The target screen is no longer valid.
state = OUTSIDE;
position.set(-1, -1);
}
} else {
position.subtract(configuration.bounds.top_left());
}
}
if (state == INSIDE) {
// Convert Density Independent Pixel to physical pixel.
position = DesktopVector(round(position.x() * scale),
round(position.y() * scale));
}
// Convert Density Independent Pixel to physical pixel.
configuration_monitor_->Lock();
float scale =
configuration_monitor_->desktop_configuration().dip_to_pixel_scale;
configuration_monitor_->Unlock();
position = DesktopVector(round(position.x() * scale),
round(position.y() * scale));
callback_->OnMouseCursorPosition(state, position);
}
@ -221,16 +265,15 @@ void MouseCursorMonitorMac::CaptureImage() {
callback_->OnMouseCursor(cursor.release());
}
MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
const DesktopCaptureOptions& options, WindowId window) {
return new MouseCursorMonitorMac(options, window);
return new MouseCursorMonitorMac(options, window, kInvalidScreenId);
}
MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
const DesktopCaptureOptions& options,
ScreenId screen) {
return new MouseCursorMonitorMac(options, kCGNullWindowID);
return new MouseCursorMonitorMac(options, kCGNullWindowID, screen);
}
} // namespace webrtc

View File

@ -19,7 +19,6 @@
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <OpenGL/CGLMacro.h>
#include <OpenGL/OpenGL.h>
#include <sys/utsname.h>
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
@ -27,6 +26,7 @@
#include "webrtc/modules/desktop_capture/desktop_region.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
#include "webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h"
#include "webrtc/modules/desktop_capture/mac/osx_version.h"
#include "webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.h"
#include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
@ -87,39 +87,6 @@ void CopyRect(const uint8_t* src_plane,
}
}
int GetDarwinVersion() {
struct utsname uname_info;
if (uname(&uname_info) != 0) {
LOG(LS_ERROR) << "uname failed";
return 0;
}
if (strcmp(uname_info.sysname, "Darwin") != 0)
return 0;
char* dot;
int result = strtol(uname_info.release, &dot, 10);
if (*dot != '.') {
LOG(LS_ERROR) << "Failed to parse version";
return 0;
}
return result;
}
bool IsOSLionOrLater() {
static int darwin_version = GetDarwinVersion();
// Verify that the version has been parsed correctly.
if (darwin_version < 6) {
LOG_F(LS_ERROR) << "Invalid Darwin version: " << darwin_version;
abort();
}
// Darwin major version 11 corresponds to OSX 10.7.
return darwin_version >= 11;
}
// A class to perform video frame capturing for mac.
class ScreenCapturerMac : public ScreenCapturer {
public:
@ -145,7 +112,8 @@ class ScreenCapturerMac : public ScreenCapturer {
void GlBlitSlow(const DesktopFrame& frame);
void CgBlitPreLion(const DesktopFrame& frame,
const DesktopRegion& region);
void CgBlitPostLion(const DesktopFrame& frame,
// Returns false if the selected screen is no longer valid.
bool CgBlitPostLion(const DesktopFrame& frame,
const DesktopRegion& region);
// Called when the screen configuration is changed.
@ -167,6 +135,8 @@ class ScreenCapturerMac : public ScreenCapturer {
void *user_parameter);
void ReleaseBuffers();
DesktopFrame* CreateFrame();
Callback* callback_;
MouseShapeObserver* mouse_shape_observer_;
@ -176,6 +146,19 @@ class ScreenCapturerMac : public ScreenCapturer {
// Queue of the frames buffers.
ScreenCaptureFrameQueue queue_;
// Current display configuration.
MacDesktopConfiguration desktop_config_;
// Currently selected display, or 0 if the full desktop is selected. On OS X
// 10.6 and before, this is always 0.
CGDirectDisplayID current_display_;
// The physical pixel bounds of the current screen.
DesktopRect screen_pixel_bounds_;
// The dip to physical pixel scale of the current screen.
float dip_to_pixel_scale_;
// A thread-safe list of invalid rectangles, and the size of the most
// recently captured screen.
ScreenCapturerHelper helper_;
@ -189,10 +172,6 @@ class ScreenCapturerMac : public ScreenCapturer {
// Monitoring display reconfiguration.
scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
// The desktop configuration obtained from desktop_config_monitor_ the last
// time of capturing.
MacDesktopConfiguration desktop_config_;
// Power management assertion to prevent the screen from sleeping.
IOPMAssertionID power_assertion_id_display_;
@ -233,24 +212,13 @@ class InvertedDesktopFrame : public DesktopFrame {
DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
};
DesktopFrame* CreateFrame(
const MacDesktopConfiguration& desktop_config) {
DesktopSize size(desktop_config.pixel_bounds.width(),
desktop_config.pixel_bounds.height());
scoped_ptr<DesktopFrame> frame(new BasicDesktopFrame(size));
frame->set_dpi(DesktopVector(
kStandardDPI * desktop_config.dip_to_pixel_scale,
kStandardDPI * desktop_config.dip_to_pixel_scale));
return frame.release();
}
ScreenCapturerMac::ScreenCapturerMac(
scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor)
: callback_(NULL),
mouse_shape_observer_(NULL),
cgl_context_(NULL),
current_display_(0),
dip_to_pixel_scale_(1.0f),
desktop_config_monitor_(desktop_config_monitor),
power_assertion_id_display_(kIOPMNullAssertionID),
power_assertion_id_user_(kIOPMNullAssertionID),
@ -282,6 +250,9 @@ bool ScreenCapturerMac::Init() {
if (!RegisterRefreshAndMoveHandlers()) {
return false;
}
desktop_config_monitor_->Lock();
desktop_config_ = desktop_config_monitor_->desktop_configuration();
desktop_config_monitor_->Unlock();
ScreenConfigurationChanged();
return true;
}
@ -346,7 +317,7 @@ void ScreenCapturerMac::Capture(
// Note that we can't reallocate other buffers at this point, since the caller
// may still be reading from them.
if (!queue_.current_frame())
queue_.ReplaceCurrentFrame(CreateFrame(desktop_config_));
queue_.ReplaceCurrentFrame(CreateFrame());
DesktopFrame* current_frame = queue_.current_frame();
@ -354,7 +325,10 @@ void ScreenCapturerMac::Capture(
if (IsOSLionOrLater()) {
// Lion requires us to use their new APIs for doing screen capture. These
// APIS currently crash on 10.6.8 if there is no monitor attached.
CgBlitPostLion(*current_frame, region);
if (!CgBlitPostLion(*current_frame, region)) {
callback_->OnCaptureCompleted(NULL);
return;
}
} else if (cgl_context_) {
flip = true;
if (pixel_buffer_object_.get() != 0) {
@ -397,15 +371,42 @@ void ScreenCapturerMac::SetMouseShapeObserver(
bool ScreenCapturerMac::GetScreenList(ScreenList* screens) {
assert(screens->size() == 0);
// TODO(jiayl): implement screen enumeration.
Screen default_screen;
default_screen.id = 0;
screens->push_back(default_screen);
if (!IsOSLionOrLater()) {
// Single monitor cast is not supported on pre OS X 10.7.
Screen screen;
screen.id = kFullDesktopScreenId;
screens->push_back(screen);
return true;
}
for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
it != desktop_config_.displays.end(); ++it) {
Screen screen;
screen.id = static_cast<ScreenId>(it->id);
screens->push_back(screen);
}
return true;
}
bool ScreenCapturerMac::SelectScreen(ScreenId id) {
// TODO(jiayl): implement screen selection.
if (!IsOSLionOrLater()) {
// Ignore the screen selection on unsupported OS.
assert(!current_display_);
return id == kFullDesktopScreenId;
}
if (id == kFullDesktopScreenId) {
current_display_ = 0;
} else {
const MacDisplayConfiguration* config =
desktop_config_.FindDisplayConfigurationById(
static_cast<CGDirectDisplayID>(id));
if (!config)
return false;
current_display_ = config->id;
}
ScreenConfigurationChanged();
return true;
}
@ -602,7 +603,7 @@ void ScreenCapturerMac::CgBlitPreLion(const DesktopFrame& frame,
}
}
void ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
bool ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
const DesktopRegion& region) {
// Copy the entire contents of the previous capture buffer, to capture over.
// TODO(wez): Get rid of this as per crbug.com/145064, or implement
@ -613,13 +614,37 @@ void ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
frame.stride() * frame.size().height());
}
for (size_t i = 0; i < desktop_config_.displays.size(); ++i) {
const MacDisplayConfiguration& display_config = desktop_config_.displays[i];
MacDisplayConfigurations displays_to_capture;
if (current_display_) {
// Capturing a single screen. Note that the screen id may change when
// screens are added or removed.
const MacDisplayConfiguration* config =
desktop_config_.FindDisplayConfigurationById(current_display_);
if (config) {
displays_to_capture.push_back(*config);
} else {
LOG(LS_ERROR) << "The selected screen cannot be found for capturing.";
return false;
}
} else {
// Capturing the whole desktop.
displays_to_capture = desktop_config_.displays;
}
for (size_t i = 0; i < displays_to_capture.size(); ++i) {
const MacDisplayConfiguration& display_config = displays_to_capture[i];
// Capturing mixed-DPI on one surface is hard, so we only return displays
// that match the "primary" display's DPI. The primary display is always
// the first in the list.
if (i > 0 && display_config.dip_to_pixel_scale !=
displays_to_capture[0].dip_to_pixel_scale) {
continue;
}
// Determine the display's position relative to the desktop, in pixels.
DesktopRect display_bounds = display_config.pixel_bounds;
display_bounds.Translate(-desktop_config_.pixel_bounds.left(),
-desktop_config_.pixel_bounds.top());
display_bounds.Translate(-screen_pixel_bounds_.left(),
-screen_pixel_bounds_.top());
// Determine which parts of the blit region, if any, lay within the monitor.
DesktopRegion copy_region = region;
@ -662,9 +687,20 @@ void ScreenCapturerMac::CgBlitPostLion(const DesktopFrame& frame,
CFRelease(data);
CFRelease(image);
}
return true;
}
void ScreenCapturerMac::ScreenConfigurationChanged() {
if (current_display_) {
const MacDisplayConfiguration* config =
desktop_config_.FindDisplayConfigurationById(current_display_);
screen_pixel_bounds_ = config ? config->pixel_bounds : DesktopRect();
dip_to_pixel_scale_ = config ? config->dip_to_pixel_scale : 1.0f;
} else {
screen_pixel_bounds_ = desktop_config_.pixel_bounds;
dip_to_pixel_scale_ = desktop_config_.dip_to_pixel_scale;
}
// Release existing buffers, which will be of the wrong size.
ReleaseBuffers();
@ -672,7 +708,7 @@ void ScreenCapturerMac::ScreenConfigurationChanged() {
helper_.ClearInvalidRegion();
// Re-mark the entire desktop as dirty.
helper_.InvalidateScreen(desktop_config_.pixel_bounds.size());
helper_.InvalidateScreen(screen_pixel_bounds_.size());
// Make sure the frame buffers will be reallocated.
queue_.Reset();
@ -753,8 +789,8 @@ void ScreenCapturerMac::ScreenConfigurationChanged() {
(*cgl_set_full_screen_)(cgl_context_);
CGLSetCurrentContext(cgl_context_);
size_t buffer_size = desktop_config_.pixel_bounds.width() *
desktop_config_.pixel_bounds.height() *
size_t buffer_size = screen_pixel_bounds_.width() *
screen_pixel_bounds_.height() *
sizeof(uint32_t);
pixel_buffer_object_.Init(cgl_context_, buffer_size);
}
@ -786,20 +822,17 @@ void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
void ScreenCapturerMac::ScreenRefresh(CGRectCount count,
const CGRect* rect_array) {
if (desktop_config_.pixel_bounds.is_empty())
if (screen_pixel_bounds_.is_empty())
return;
DesktopRegion region;
DesktopVector translate_vector =
DesktopVector().subtract(screen_pixel_bounds_.top_left());
for (CGRectCount i = 0; i < count; ++i) {
// Convert from Density-Independent Pixel to physical pixel coordinates.
DesktopRect rect =
ScaleAndRoundCGRect(rect_array[i], desktop_config_.dip_to_pixel_scale);
DesktopRect rect = ScaleAndRoundCGRect(rect_array[i], dip_to_pixel_scale_);
// Translate from local desktop to capturer framebuffer coordinates.
rect.Translate(-desktop_config_.pixel_bounds.left(),
-desktop_config_.pixel_bounds.top());
rect.Translate(translate_vector);
region.AddRect(rect);
}
@ -824,7 +857,7 @@ void ScreenCapturerMac::ScreenRefreshCallback(CGRectCount count,
void* user_parameter) {
ScreenCapturerMac* capturer =
reinterpret_cast<ScreenCapturerMac*>(user_parameter);
if (capturer->desktop_config_.pixel_bounds.is_empty())
if (capturer->screen_pixel_bounds_.is_empty())
capturer->ScreenConfigurationChanged();
capturer->ScreenRefresh(count, rect_array);
}
@ -839,6 +872,15 @@ void ScreenCapturerMac::ScreenUpdateMoveCallback(
capturer->ScreenUpdateMove(delta, count, rect_array);
}
DesktopFrame* ScreenCapturerMac::CreateFrame() {
scoped_ptr<DesktopFrame> frame(
new BasicDesktopFrame(screen_pixel_bounds_.size()));
frame->set_dpi(DesktopVector(kStandardDPI * dip_to_pixel_scale_,
kStandardDPI * dip_to_pixel_scale_));
return frame.release();
}
} // namespace
// static