aboutsummaryrefslogtreecommitdiff
path: root/webrtc/modules/desktop_capture/win
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/modules/desktop_capture/win')
-rw-r--r--webrtc/modules/desktop_capture/win/cursor.cc248
-rw-r--r--webrtc/modules/desktop_capture/win/cursor.h25
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.curbin0 -> 3262 bytes
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.curbin0 -> 4286 bytes
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.curbin0 -> 2238 bytes
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.curbin0 -> 326 bytes
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.curbin0 -> 4286 bytes
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.curbin0 -> 4286 bytes
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.curbin0 -> 766 bytes
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_unittest.cc89
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_unittest_resources.h24
-rw-r--r--webrtc/modules/desktop_capture/win/cursor_unittest_resources.rc28
-rw-r--r--webrtc/modules/desktop_capture/win/desktop.cc110
-rw-r--r--webrtc/modules/desktop_capture/win/desktop.h64
-rw-r--r--webrtc/modules/desktop_capture/win/scoped_gdi_object.h95
-rw-r--r--webrtc/modules/desktop_capture/win/scoped_thread_desktop.cc57
-rw-r--r--webrtc/modules/desktop_capture/win/scoped_thread_desktop.h53
-rw-r--r--webrtc/modules/desktop_capture/win/screen_capture_utils.cc92
-rw-r--r--webrtc/modules/desktop_capture/win/screen_capture_utils.h35
-rw-r--r--webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc270
-rw-r--r--webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h89
-rw-r--r--webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc449
-rw-r--r--webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h153
-rw-r--r--webrtc/modules/desktop_capture/win/window_capture_utils.cc69
-rw-r--r--webrtc/modules/desktop_capture/win/window_capture_utils.h40
25 files changed, 1990 insertions, 0 deletions
diff --git a/webrtc/modules/desktop_capture/win/cursor.cc b/webrtc/modules/desktop_capture/win/cursor.cc
new file mode 100644
index 0000000000..a3acaf822b
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor.cc
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2013 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 "webrtc/modules/desktop_capture/win/cursor.h"
+
+#include <algorithm>
+
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/desktop_geometry.h"
+#include "webrtc/modules/desktop_capture/mouse_cursor.h"
+#include "webrtc/system_wrappers/include/logging.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+namespace {
+
+#if defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+#define RGBA(r, g, b, a) \
+ ((((a) << 24) & 0xff000000) | \
+ (((b) << 16) & 0xff0000) | \
+ (((g) << 8) & 0xff00) | \
+ ((r) & 0xff))
+
+#else // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+#define RGBA(r, g, b, a) \
+ ((((r) << 24) & 0xff000000) | \
+ (((g) << 16) & 0xff0000) | \
+ (((b) << 8) & 0xff00) | \
+ ((a) & 0xff))
+
+#endif // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+const int kBytesPerPixel = DesktopFrame::kBytesPerPixel;
+
+// Pixel colors used when generating cursor outlines.
+const uint32_t kPixelRgbaBlack = RGBA(0, 0, 0, 0xff);
+const uint32_t kPixelRgbaWhite = RGBA(0xff, 0xff, 0xff, 0xff);
+const uint32_t kPixelRgbaTransparent = RGBA(0, 0, 0, 0);
+
+const uint32_t kPixelRgbWhite = RGB(0xff, 0xff, 0xff);
+
+// Expands the cursor shape to add a white outline for visibility against
+// dark backgrounds.
+void AddCursorOutline(int width, int height, uint32_t* data) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // If this is a transparent pixel (bgr == 0 and alpha = 0), check the
+ // neighbor pixels to see if this should be changed to an outline pixel.
+ if (*data == kPixelRgbaTransparent) {
+ // Change to white pixel if any neighbors (top, bottom, left, right)
+ // are black.
+ if ((y > 0 && data[-width] == kPixelRgbaBlack) ||
+ (y < height - 1 && data[width] == kPixelRgbaBlack) ||
+ (x > 0 && data[-1] == kPixelRgbaBlack) ||
+ (x < width - 1 && data[1] == kPixelRgbaBlack)) {
+ *data = kPixelRgbaWhite;
+ }
+ }
+ data++;
+ }
+ }
+}
+
+// Premultiplies RGB components of the pixel data in the given image by
+// the corresponding alpha components.
+void AlphaMul(uint32_t* data, int width, int height) {
+ static_assert(sizeof(uint32_t) == kBytesPerPixel,
+ "size of uint32 should be the number of bytes per pixel");
+
+ for (uint32_t* data_end = data + width * height; data != data_end; ++data) {
+ RGBQUAD* from = reinterpret_cast<RGBQUAD*>(data);
+ RGBQUAD* to = reinterpret_cast<RGBQUAD*>(data);
+ to->rgbBlue =
+ (static_cast<uint16_t>(from->rgbBlue) * from->rgbReserved) / 0xff;
+ to->rgbGreen =
+ (static_cast<uint16_t>(from->rgbGreen) * from->rgbReserved) / 0xff;
+ to->rgbRed =
+ (static_cast<uint16_t>(from->rgbRed) * from->rgbReserved) / 0xff;
+ }
+}
+
+// Scans a 32bpp bitmap looking for any pixels with non-zero alpha component.
+// Returns true if non-zero alpha is found. |stride| is expressed in pixels.
+bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height) {
+ const RGBQUAD* plane = reinterpret_cast<const RGBQUAD*>(data);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ if (plane->rgbReserved != 0)
+ return true;
+ plane += 1;
+ }
+ plane += stride - width;
+ }
+
+ return false;
+}
+
+} // namespace
+
+MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor) {
+ ICONINFO iinfo;
+ if (!GetIconInfo(cursor, &iinfo)) {
+ LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ int hotspot_x = iinfo.xHotspot;
+ int hotspot_y = iinfo.yHotspot;
+
+ // Make sure the bitmaps will be freed.
+ win::ScopedBitmap scoped_mask(iinfo.hbmMask);
+ win::ScopedBitmap scoped_color(iinfo.hbmColor);
+ bool is_color = iinfo.hbmColor != NULL;
+
+ // Get |scoped_mask| dimensions.
+ BITMAP bitmap_info;
+ if (!GetObject(scoped_mask, sizeof(bitmap_info), &bitmap_info)) {
+ LOG_F(LS_ERROR) << "Unable to get bitmap info. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ int width = bitmap_info.bmWidth;
+ int height = bitmap_info.bmHeight;
+ rtc::scoped_ptr<uint32_t[]> mask_data(new uint32_t[width * height]);
+
+ // Get pixel data from |scoped_mask| converting it to 32bpp along the way.
+ // GetDIBits() sets the alpha component of every pixel to 0.
+ BITMAPV5HEADER bmi = {0};
+ bmi.bV5Size = sizeof(bmi);
+ bmi.bV5Width = width;
+ bmi.bV5Height = -height; // request a top-down bitmap.
+ bmi.bV5Planes = 1;
+ bmi.bV5BitCount = kBytesPerPixel * 8;
+ bmi.bV5Compression = BI_RGB;
+ bmi.bV5AlphaMask = 0xff000000;
+ bmi.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
+ bmi.bV5Intent = LCS_GM_BUSINESS;
+ if (!GetDIBits(dc,
+ scoped_mask,
+ 0,
+ height,
+ mask_data.get(),
+ reinterpret_cast<BITMAPINFO*>(&bmi),
+ DIB_RGB_COLORS)) {
+ LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ uint32_t* mask_plane = mask_data.get();
+ rtc::scoped_ptr<DesktopFrame> image(
+ new BasicDesktopFrame(DesktopSize(width, height)));
+ bool has_alpha = false;
+
+ if (is_color) {
+ image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
+ // Get the pixels from the color bitmap.
+ if (!GetDIBits(dc,
+ scoped_color,
+ 0,
+ height,
+ image->data(),
+ reinterpret_cast<BITMAPINFO*>(&bmi),
+ DIB_RGB_COLORS)) {
+ LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ // GetDIBits() does not provide any indication whether the bitmap has alpha
+ // channel, so we use HasAlphaChannel() below to find it out.
+ has_alpha = HasAlphaChannel(reinterpret_cast<uint32_t*>(image->data()),
+ width, width, height);
+ } else {
+ // For non-color cursors, the mask contains both an AND and an XOR mask and
+ // the height includes both. Thus, the width is correct, but we need to
+ // divide by 2 to get the correct mask height.
+ height /= 2;
+
+ image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
+
+ // The XOR mask becomes the color bitmap.
+ memcpy(
+ image->data(), mask_plane + (width * height), image->stride() * height);
+ }
+
+ // Reconstruct transparency from the mask if the color image does not has
+ // alpha channel.
+ if (!has_alpha) {
+ bool add_outline = false;
+ uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
+ uint32_t* mask = mask_plane;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // The two bitmaps combine as follows:
+ // mask color Windows Result Our result RGB Alpha
+ // 0 00 Black Black 00 ff
+ // 0 ff White White ff ff
+ // 1 00 Screen Transparent 00 00
+ // 1 ff Reverse-screen Black 00 ff
+ //
+ // Since we don't support XOR cursors, we replace the "Reverse Screen"
+ // with black. In this case, we also add an outline around the cursor
+ // so that it is visible against a dark background.
+ if (*mask == kPixelRgbWhite) {
+ if (*dst != 0) {
+ add_outline = true;
+ *dst = kPixelRgbaBlack;
+ } else {
+ *dst = kPixelRgbaTransparent;
+ }
+ } else {
+ *dst = kPixelRgbaBlack ^ *dst;
+ }
+
+ ++dst;
+ ++mask;
+ }
+ }
+ if (add_outline) {
+ AddCursorOutline(
+ width, height, reinterpret_cast<uint32_t*>(image->data()));
+ }
+ }
+
+ // Pre-multiply the resulting pixels since MouseCursor uses premultiplied
+ // images.
+ AlphaMul(reinterpret_cast<uint32_t*>(image->data()), width, height);
+
+ return new MouseCursor(
+ image.release(), DesktopVector(hotspot_x, hotspot_y));
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/cursor.h b/webrtc/modules/desktop_capture/win/cursor.h
new file mode 100644
index 0000000000..d521cc0819
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
+
+#include <windows.h>
+
+namespace webrtc {
+
+class MouseCursor;
+
+// Converts an HCURSOR into a |MouseCursor| instance.
+MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor);
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
diff --git a/webrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur b/webrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur
new file mode 100644
index 0000000000..27702b825c
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur
Binary files differ
diff --git a/webrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur b/webrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur
new file mode 100644
index 0000000000..7e0d8596da
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur
Binary files differ
diff --git a/webrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur b/webrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur
new file mode 100644
index 0000000000..fefb09e1a1
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur
Binary files differ
diff --git a/webrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur b/webrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur
new file mode 100644
index 0000000000..4f8a094f31
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur
Binary files differ
diff --git a/webrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur b/webrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur
new file mode 100644
index 0000000000..ac9cdbfbb3
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur
Binary files differ
diff --git a/webrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur b/webrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur
new file mode 100644
index 0000000000..efdbee5415
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur
Binary files differ
diff --git a/webrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur b/webrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur
new file mode 100644
index 0000000000..9678d55446
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur
Binary files differ
diff --git a/webrtc/modules/desktop_capture/win/cursor_unittest.cc b/webrtc/modules/desktop_capture/win/cursor_unittest.cc
new file mode 100644
index 0000000000..32bab13e00
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_unittest.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013 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 "testing/gmock/include/gmock/gmock.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/desktop_geometry.h"
+#include "webrtc/modules/desktop_capture/mouse_cursor.h"
+#include "webrtc/modules/desktop_capture/win/cursor.h"
+#include "webrtc/modules/desktop_capture/win/cursor_unittest_resources.h"
+#include "webrtc/modules/desktop_capture/win/scoped_gdi_object.h"
+
+namespace webrtc {
+
+namespace {
+
+// Loads |left| from resources, converts it to a |MouseCursor| instance and
+// compares pixels with |right|. Returns true of MouseCursor bits match |right|.
+// |right| must be a 32bpp cursor with alpha channel.
+bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
+ HMODULE instance = GetModuleHandle(NULL);
+
+ // Load |left| from the EXE module's resources.
+ win::ScopedCursor cursor(reinterpret_cast<HCURSOR>(
+ LoadImage(instance, MAKEINTRESOURCE(left), IMAGE_CURSOR, 0, 0, 0)));
+ EXPECT_TRUE(cursor != NULL);
+
+ // Convert |cursor| to |mouse_shape|.
+ HDC dc = GetDC(NULL);
+ rtc::scoped_ptr<MouseCursor> mouse_shape(
+ CreateMouseCursorFromHCursor(dc, cursor));
+ ReleaseDC(NULL, dc);
+
+ EXPECT_TRUE(mouse_shape.get());
+
+ // Load |right|.
+ cursor.Set(reinterpret_cast<HCURSOR>(
+ LoadImage(instance, MAKEINTRESOURCE(right), IMAGE_CURSOR, 0, 0, 0)));
+
+ ICONINFO iinfo;
+ EXPECT_TRUE(GetIconInfo(cursor, &iinfo));
+ EXPECT_TRUE(iinfo.hbmColor);
+
+ // Make sure the bitmaps will be freed.
+ win::ScopedBitmap scoped_mask(iinfo.hbmMask);
+ win::ScopedBitmap scoped_color(iinfo.hbmColor);
+
+ // Get |scoped_color| dimensions.
+ BITMAP bitmap_info;
+ EXPECT_TRUE(GetObject(scoped_color, sizeof(bitmap_info), &bitmap_info));
+
+ int width = bitmap_info.bmWidth;
+ int height = bitmap_info.bmHeight;
+ EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->image()->size()));
+
+ // Get the pixels from |scoped_color|.
+ int size = width * height;
+ rtc::scoped_ptr<uint32_t[]> data(new uint32_t[size]);
+ EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get()));
+
+ // Compare the 32bpp image in |mouse_shape| with the one loaded from |right|.
+ return memcmp(data.get(), mouse_shape->image()->data(),
+ size * sizeof(uint32_t)) == 0;
+}
+
+} // namespace
+
+TEST(MouseCursorTest, MatchCursors) {
+ EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP,
+ IDD_CURSOR1_32BPP));
+
+ EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR1_8BPP,
+ IDD_CURSOR1_32BPP));
+
+ EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR2_1BPP,
+ IDD_CURSOR2_32BPP));
+
+ EXPECT_TRUE(ConvertToMouseShapeAndCompare(IDD_CURSOR3_4BPP,
+ IDD_CURSOR3_32BPP));
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/cursor_unittest_resources.h b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.h
new file mode 100644
index 0000000000..89545c165f
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
+
+#define IDD_CURSOR1_24BPP 101
+#define IDD_CURSOR1_32BPP 102
+#define IDD_CURSOR1_8BPP 103
+
+#define IDD_CURSOR2_1BPP 104
+#define IDD_CURSOR2_32BPP 105
+
+#define IDD_CURSOR3_4BPP 106
+#define IDD_CURSOR3_32BPP 107
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
diff --git a/webrtc/modules/desktop_capture/win/cursor_unittest_resources.rc b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.rc
new file mode 100644
index 0000000000..4f41489ecd
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/cursor_unittest_resources.rc
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 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 "webrtc/modules/desktop_capture/win/cursor_unittest_resources.h"
+
+// These cursors are matched with their less than 32bpp counterparts below.
+IDD_CURSOR1_32BPP CURSOR "cursor_test_data/1_32bpp.cur"
+IDD_CURSOR2_32BPP CURSOR "cursor_test_data/2_32bpp.cur"
+IDD_CURSOR3_32BPP CURSOR "cursor_test_data/3_32bpp.cur"
+
+// Matches IDD_CURSOR1_32BPP.
+IDD_CURSOR1_24BPP CURSOR "cursor_test_data/1_24bpp.cur"
+
+// Matches IDD_CURSOR1_32BPP.
+IDD_CURSOR1_8BPP CURSOR "cursor_test_data/1_8bpp.cur"
+
+// Matches IDD_CURSOR2_32BPP.
+IDD_CURSOR2_1BPP CURSOR "cursor_test_data/2_1bpp.cur"
+
+// Matches IDD_CURSOR3_32BPP.
+IDD_CURSOR3_4BPP CURSOR "cursor_test_data/3_4bpp.cur"
diff --git a/webrtc/modules/desktop_capture/win/desktop.cc b/webrtc/modules/desktop_capture/win/desktop.cc
new file mode 100644
index 0000000000..97bbfb717b
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/desktop.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2013 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 "webrtc/modules/desktop_capture/win/desktop.h"
+
+#include <vector>
+
+#include "webrtc/system_wrappers/include/logging.h"
+
+namespace webrtc {
+
+Desktop::Desktop(HDESK desktop, bool own) : desktop_(desktop), own_(own) {
+}
+
+Desktop::~Desktop() {
+ if (own_ && desktop_ != NULL) {
+ if (!::CloseDesktop(desktop_)) {
+ LOG(LS_ERROR) << "Failed to close the owned desktop handle: "
+ << GetLastError();
+ }
+ }
+}
+
+bool Desktop::GetName(std::wstring* desktop_name_out) const {
+ if (desktop_ == NULL)
+ return false;
+
+ DWORD length = 0;
+ int rv = GetUserObjectInformationW(desktop_, UOI_NAME, NULL, 0, &length);
+ if (rv || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ abort();
+
+ length /= sizeof(WCHAR);
+ std::vector<WCHAR> buffer(length);
+ if (!GetUserObjectInformationW(desktop_, UOI_NAME, &buffer[0],
+ length * sizeof(WCHAR), &length)) {
+ LOG(LS_ERROR) << "Failed to query the desktop name: " << GetLastError();
+ return false;
+ }
+
+ desktop_name_out->assign(&buffer[0], length / sizeof(WCHAR));
+ return true;
+}
+
+bool Desktop::IsSame(const Desktop& other) const {
+ std::wstring name;
+ if (!GetName(&name))
+ return false;
+
+ std::wstring other_name;
+ if (!other.GetName(&other_name))
+ return false;
+
+ return name == other_name;
+}
+
+bool Desktop::SetThreadDesktop() const {
+ if (!::SetThreadDesktop(desktop_)) {
+ LOG(LS_ERROR) << "Failed to assign the desktop to the current thread: "
+ << GetLastError();
+ return false;
+ }
+
+ return true;
+}
+
+Desktop* Desktop::GetDesktop(const WCHAR* desktop_name) {
+ ACCESS_MASK desired_access =
+ DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE |
+ DESKTOP_HOOKCONTROL | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE;
+ HDESK desktop = OpenDesktop(desktop_name, 0, FALSE, desired_access);
+ if (desktop == NULL) {
+ LOG(LS_ERROR) << "Failed to open the desktop '" << desktop_name << "': "
+ << GetLastError();
+ return NULL;
+ }
+
+ return new Desktop(desktop, true);
+}
+
+Desktop* Desktop::GetInputDesktop() {
+ HDESK desktop = OpenInputDesktop(
+ 0, FALSE, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
+ if (desktop == NULL)
+ return NULL;
+
+ return new Desktop(desktop, true);
+}
+
+Desktop* Desktop::GetThreadDesktop() {
+ HDESK desktop = ::GetThreadDesktop(GetCurrentThreadId());
+ if (desktop == NULL) {
+ LOG(LS_ERROR) << "Failed to retrieve the handle of the desktop assigned to "
+ "the current thread: "
+ << GetLastError();
+ return NULL;
+ }
+
+ return new Desktop(desktop, false);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/desktop.h b/webrtc/modules/desktop_capture/win/desktop.h
new file mode 100644
index 0000000000..dc3b8c61b9
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/desktop.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
+
+#include <windows.h>
+#include <string>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace webrtc {
+
+class Desktop {
+ public:
+ ~Desktop();
+
+ // Returns the name of the desktop represented by the object. Return false if
+ // quering the name failed for any reason.
+ bool GetName(std::wstring* desktop_name_out) const;
+
+ // Returns true if |other| has the same name as this desktop. Returns false
+ // in any other case including failing Win32 APIs and uninitialized desktop
+ // handles.
+ bool IsSame(const Desktop& other) const;
+
+ // Assigns the desktop to the current thread. Returns false is the operation
+ // failed for any reason.
+ bool SetThreadDesktop() const;
+
+ // Returns the desktop by its name or NULL if an error occurs.
+ static Desktop* GetDesktop(const wchar_t* desktop_name);
+
+ // Returns the desktop currently receiving user input or NULL if an error
+ // occurs.
+ static Desktop* GetInputDesktop();
+
+ // Returns the desktop currently assigned to the calling thread or NULL if
+ // an error occurs.
+ static Desktop* GetThreadDesktop();
+
+ private:
+ Desktop(HDESK desktop, bool own);
+
+ // The desktop handle.
+ HDESK desktop_;
+
+ // True if |desktop_| must be closed on teardown.
+ bool own_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(Desktop);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
diff --git a/webrtc/modules/desktop_capture/win/scoped_gdi_object.h b/webrtc/modules/desktop_capture/win/scoped_gdi_object.h
new file mode 100644
index 0000000000..1cac63e43d
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/scoped_gdi_object.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
+
+#include <windows.h>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace win {
+
+// Scoper for GDI objects.
+template<class T, class Traits>
+class ScopedGDIObject {
+ public:
+ ScopedGDIObject() : handle_(NULL) {}
+ explicit ScopedGDIObject(T object) : handle_(object) {}
+
+ ~ScopedGDIObject() {
+ Traits::Close(handle_);
+ }
+
+ T Get() {
+ return handle_;
+ }
+
+ void Set(T object) {
+ if (handle_ && object != handle_)
+ Traits::Close(handle_);
+ handle_ = object;
+ }
+
+ ScopedGDIObject& operator=(T object) {
+ Set(object);
+ return *this;
+ }
+
+ T release() {
+ T object = handle_;
+ handle_ = NULL;
+ return object;
+ }
+
+ operator T() { return handle_; }
+
+ private:
+ T handle_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject);
+};
+
+// The traits class that uses DeleteObject() to close a handle.
+template <typename T>
+class DeleteObjectTraits {
+ public:
+ // Closes the handle.
+ static void Close(T handle) {
+ if (handle)
+ DeleteObject(handle);
+ }
+
+ private:
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(DeleteObjectTraits);
+};
+
+// The traits class that uses DestroyCursor() to close a handle.
+class DestroyCursorTraits {
+ public:
+ // Closes the handle.
+ static void Close(HCURSOR handle) {
+ if (handle)
+ DestroyCursor(handle);
+ }
+
+ private:
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(DestroyCursorTraits);
+};
+
+typedef ScopedGDIObject<HBITMAP, DeleteObjectTraits<HBITMAP> > ScopedBitmap;
+typedef ScopedGDIObject<HCURSOR, DestroyCursorTraits> ScopedCursor;
+
+} // namespace win
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
diff --git a/webrtc/modules/desktop_capture/win/scoped_thread_desktop.cc b/webrtc/modules/desktop_capture/win/scoped_thread_desktop.cc
new file mode 100644
index 0000000000..12f9e89e96
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/scoped_thread_desktop.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2013 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 "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
+
+#include "webrtc/system_wrappers/include/logging.h"
+
+#include "webrtc/modules/desktop_capture/win/desktop.h"
+
+namespace webrtc {
+
+ScopedThreadDesktop::ScopedThreadDesktop()
+ : initial_(Desktop::GetThreadDesktop()) {
+}
+
+ScopedThreadDesktop::~ScopedThreadDesktop() {
+ Revert();
+}
+
+bool ScopedThreadDesktop::IsSame(const Desktop& desktop) {
+ if (assigned_.get() != NULL) {
+ return assigned_->IsSame(desktop);
+ } else {
+ return initial_->IsSame(desktop);
+ }
+}
+
+void ScopedThreadDesktop::Revert() {
+ if (assigned_.get() != NULL) {
+ initial_->SetThreadDesktop();
+ assigned_.reset();
+ }
+}
+
+bool ScopedThreadDesktop::SetThreadDesktop(Desktop* desktop) {
+ Revert();
+
+ rtc::scoped_ptr<Desktop> scoped_desktop(desktop);
+
+ if (initial_->IsSame(*desktop))
+ return true;
+
+ if (!desktop->SetThreadDesktop())
+ return false;
+
+ assigned_.reset(scoped_desktop.release());
+ return true;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h b/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h
new file mode 100644
index 0000000000..df8652ac9d
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
+
+#include <windows.h>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ptr.h"
+
+namespace webrtc {
+
+class Desktop;
+
+class ScopedThreadDesktop {
+ public:
+ ScopedThreadDesktop();
+ ~ScopedThreadDesktop();
+
+ // Returns true if |desktop| has the same desktop name as the currently
+ // assigned desktop (if assigned) or as the initial desktop (if not assigned).
+ // Returns false in any other case including failing Win32 APIs and
+ // uninitialized desktop handles.
+ bool IsSame(const Desktop& desktop);
+
+ // Reverts the calling thread to use the initial desktop.
+ void Revert();
+
+ // Assigns |desktop| to be the calling thread. Returns true if the thread has
+ // been switched to |desktop| successfully. Takes ownership of |desktop|.
+ bool SetThreadDesktop(Desktop* desktop);
+
+ private:
+ // The desktop handle assigned to the calling thread by Set
+ rtc::scoped_ptr<Desktop> assigned_;
+
+ // The desktop handle assigned to the calling thread at creation.
+ rtc::scoped_ptr<Desktop> initial_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(ScopedThreadDesktop);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
diff --git a/webrtc/modules/desktop_capture/win/screen_capture_utils.cc b/webrtc/modules/desktop_capture/win/screen_capture_utils.cc
new file mode 100644
index 0000000000..1b33545277
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/screen_capture_utils.cc
@@ -0,0 +1,92 @@
+/*
+ * 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 "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
+
+#include <assert.h>
+#include <windows.h>
+
+namespace webrtc {
+
+bool GetScreenList(ScreenCapturer::ScreenList* screens) {
+ assert(screens->size() == 0);
+
+ BOOL enum_result = TRUE;
+ for (int device_index = 0;; ++device_index) {
+ DISPLAY_DEVICE device;
+ device.cb = sizeof(device);
+ enum_result = EnumDisplayDevices(NULL, device_index, &device, 0);
+
+ // |enum_result| is 0 if we have enumerated all devices.
+ if (!enum_result)
+ break;
+
+ // We only care about active displays.
+ if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE))
+ continue;
+
+ ScreenCapturer::Screen screen;
+ screen.id = device_index;
+ screens->push_back(screen);
+ }
+ return true;
+}
+
+bool IsScreenValid(ScreenId screen, std::wstring* device_key) {
+ if (screen == kFullDesktopScreenId) {
+ *device_key = L"";
+ return true;
+ }
+
+ DISPLAY_DEVICE device;
+ device.cb = sizeof(device);
+ BOOL enum_result = EnumDisplayDevices(NULL, screen, &device, 0);
+ if (enum_result)
+ *device_key = device.DeviceKey;
+
+ return !!enum_result;
+}
+
+DesktopRect GetScreenRect(ScreenId screen, const std::wstring& device_key) {
+ if (screen == kFullDesktopScreenId) {
+ return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
+ GetSystemMetrics(SM_YVIRTUALSCREEN),
+ GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ GetSystemMetrics(SM_CYVIRTUALSCREEN));
+ }
+
+ DISPLAY_DEVICE device;
+ device.cb = sizeof(device);
+ BOOL result = EnumDisplayDevices(NULL, screen, &device, 0);
+ if (!result)
+ return DesktopRect();
+
+ // Verifies the device index still maps to the same display device, to make
+ // sure we are capturing the same device when devices are added or removed.
+ // DeviceKey is documented as reserved, but it actually contains the registry
+ // key for the device and is unique for each monitor, while DeviceID is not.
+ if (device_key != device.DeviceKey)
+ return DesktopRect();
+
+ DEVMODE device_mode;
+ device_mode.dmSize = sizeof(device_mode);
+ device_mode.dmDriverExtra = 0;
+ result = EnumDisplaySettingsEx(
+ device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
+ if (!result)
+ return DesktopRect();
+
+ return DesktopRect::MakeXYWH(device_mode.dmPosition.x,
+ device_mode.dmPosition.y,
+ device_mode.dmPelsWidth,
+ device_mode.dmPelsHeight);
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/screen_capture_utils.h b/webrtc/modules/desktop_capture/win/screen_capture_utils.h
new file mode 100644
index 0000000000..42473e047b
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/screen_capture_utils.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
+
+#include "webrtc/modules/desktop_capture/screen_capturer.h"
+
+namespace webrtc {
+
+// Output the list of active screens into |screens|. Returns true if succeeded,
+// or false if it fails to enumerate the display devices.
+bool GetScreenList(ScreenCapturer::ScreenList* screens);
+
+// Returns true if |screen| is a valid screen. The screen device key is
+// returned through |device_key| if the screen is valid. The device key can be
+// used in GetScreenRect to verify the screen matches the previously obtained
+// id.
+bool IsScreenValid(ScreenId screen, std::wstring* device_key);
+
+// Get the rect of the screen identified by |screen|, relative to the primary
+// display's top-left. If the screen device key does not match |device_key|, or
+// the screen does not exist, or any error happens, an empty rect is returned.
+DesktopRect GetScreenRect(ScreenId screen, const std::wstring& device_key);
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc b/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
new file mode 100644
index 0000000000..3cf64879f9
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
@@ -0,0 +1,270 @@
+/*
+ * 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 "webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h"
+
+#include <assert.h>
+
+#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
+#include "webrtc/modules/desktop_capture/desktop_region.h"
+#include "webrtc/modules/desktop_capture/differ.h"
+#include "webrtc/modules/desktop_capture/mouse_cursor.h"
+#include "webrtc/modules/desktop_capture/win/cursor.h"
+#include "webrtc/modules/desktop_capture/win/desktop.h"
+#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
+#include "webrtc/system_wrappers/include/logging.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+
+namespace webrtc {
+
+namespace {
+
+// Constants from dwmapi.h.
+const UINT DWM_EC_DISABLECOMPOSITION = 0;
+const UINT DWM_EC_ENABLECOMPOSITION = 1;
+
+const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
+
+} // namespace
+
+ScreenCapturerWinGdi::ScreenCapturerWinGdi(const DesktopCaptureOptions& options)
+ : callback_(NULL),
+ current_screen_id_(kFullDesktopScreenId),
+ desktop_dc_(NULL),
+ memory_dc_(NULL),
+ dwmapi_library_(NULL),
+ composition_func_(NULL),
+ set_thread_execution_state_failed_(false) {
+ if (options.disable_effects()) {
+ // Load dwmapi.dll dynamically since it is not available on XP.
+ if (!dwmapi_library_)
+ dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);
+
+ if (dwmapi_library_) {
+ composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
+ GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
+ }
+ }
+}
+
+ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
+ if (desktop_dc_)
+ ReleaseDC(NULL, desktop_dc_);
+ if (memory_dc_)
+ DeleteDC(memory_dc_);
+
+ // Restore Aero.
+ if (composition_func_)
+ (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
+
+ if (dwmapi_library_)
+ FreeLibrary(dwmapi_library_);
+}
+
+void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) {
+ TickTime capture_start_time = TickTime::Now();
+
+ queue_.MoveToNextFrame();
+
+ // Request that the system not power-down the system, or the display hardware.
+ if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
+ if (!set_thread_execution_state_failed_) {
+ set_thread_execution_state_failed_ = true;
+ LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
+ << GetLastError();
+ }
+ }
+
+ // Make sure the GDI capture resources are up-to-date.
+ PrepareCaptureResources();
+
+ if (!CaptureImage()) {
+ callback_->OnCaptureCompleted(NULL);
+ return;
+ }
+
+ const DesktopFrame* current_frame = queue_.current_frame();
+ const DesktopFrame* last_frame = queue_.previous_frame();
+ if (last_frame && last_frame->size().equals(current_frame->size())) {
+ // Make sure the differencer is set up correctly for these previous and
+ // current screens.
+ if (!differ_.get() ||
+ (differ_->width() != current_frame->size().width()) ||
+ (differ_->height() != current_frame->size().height()) ||
+ (differ_->bytes_per_row() != current_frame->stride())) {
+ differ_.reset(new Differ(current_frame->size().width(),
+ current_frame->size().height(),
+ DesktopFrame::kBytesPerPixel,
+ current_frame->stride()));
+ }
+
+ // Calculate difference between the two last captured frames.
+ DesktopRegion region;
+ differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
+ &region);
+ helper_.InvalidateRegion(region);
+ } else {
+ // No previous frame is available, or the screen is resized. Invalidate the
+ // whole screen.
+ helper_.InvalidateScreen(current_frame->size());
+ }
+
+ helper_.set_size_most_recent(current_frame->size());
+
+ // Emit the current frame.
+ DesktopFrame* frame = queue_.current_frame()->Share();
+ frame->set_dpi(DesktopVector(
+ GetDeviceCaps(desktop_dc_, LOGPIXELSX),
+ GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
+ frame->mutable_updated_region()->Clear();
+ helper_.TakeInvalidRegion(frame->mutable_updated_region());
+ frame->set_capture_time_ms(
+ (TickTime::Now() - capture_start_time).Milliseconds());
+ callback_->OnCaptureCompleted(frame);
+}
+
+bool ScreenCapturerWinGdi::GetScreenList(ScreenList* screens) {
+ return webrtc::GetScreenList(screens);
+}
+
+bool ScreenCapturerWinGdi::SelectScreen(ScreenId id) {
+ bool valid = IsScreenValid(id, &current_device_key_);
+ if (valid)
+ current_screen_id_ = id;
+ return valid;
+}
+
+void ScreenCapturerWinGdi::Start(Callback* callback) {
+ assert(!callback_);
+ assert(callback);
+
+ callback_ = callback;
+
+ // Vote to disable Aero composited desktop effects while capturing. Windows
+ // will restore Aero automatically if the process exits. This has no effect
+ // under Windows 8 or higher. See crbug.com/124018.
+ if (composition_func_)
+ (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
+}
+
+void ScreenCapturerWinGdi::PrepareCaptureResources() {
+ // Switch to the desktop receiving user input if different from the current
+ // one.
+ rtc::scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
+ if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
+ // Release GDI resources otherwise SetThreadDesktop will fail.
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = NULL;
+ }
+
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = NULL;
+ }
+
+ // If SetThreadDesktop() fails, the thread is still assigned a desktop.
+ // So we can continue capture screen bits, just from the wrong desktop.
+ desktop_.SetThreadDesktop(input_desktop.release());
+
+ // Re-assert our vote to disable Aero.
+ // See crbug.com/124018 and crbug.com/129906.
+ if (composition_func_ != NULL) {
+ (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
+ }
+ }
+
+ // If the display bounds have changed then recreate GDI resources.
+ // TODO(wez): Also check for pixel format changes.
+ DesktopRect screen_rect(DesktopRect::MakeXYWH(
+ GetSystemMetrics(SM_XVIRTUALSCREEN),
+ GetSystemMetrics(SM_YVIRTUALSCREEN),
+ GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ GetSystemMetrics(SM_CYVIRTUALSCREEN)));
+ if (!screen_rect.equals(desktop_dc_rect_)) {
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = NULL;
+ }
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = NULL;
+ }
+ desktop_dc_rect_ = DesktopRect();
+ }
+
+ if (desktop_dc_ == NULL) {
+ assert(memory_dc_ == NULL);
+
+ // Create GDI device contexts to capture from the desktop into memory.
+ desktop_dc_ = GetDC(NULL);
+ if (!desktop_dc_)
+ abort();
+ memory_dc_ = CreateCompatibleDC(desktop_dc_);
+ if (!memory_dc_)
+ abort();
+
+ desktop_dc_rect_ = screen_rect;
+
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+
+ helper_.ClearInvalidRegion();
+ }
+}
+
+bool ScreenCapturerWinGdi::CaptureImage() {
+ DesktopRect screen_rect =
+ GetScreenRect(current_screen_id_, current_device_key_);
+ if (screen_rect.is_empty())
+ return false;
+
+ DesktopSize size = screen_rect.size();
+ // If the current buffer is from an older generation then allocate a new one.
+ // 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_.current_frame()->size().equals(screen_rect.size())) {
+ assert(desktop_dc_ != NULL);
+ assert(memory_dc_ != NULL);
+
+ size_t buffer_size = size.width() * size.height() *
+ DesktopFrame::kBytesPerPixel;
+ SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size);
+
+ rtc::scoped_ptr<DesktopFrame> buffer(
+ DesktopFrameWin::Create(size, shared_memory, desktop_dc_));
+ if (!buffer.get())
+ return false;
+ queue_.ReplaceCurrentFrame(buffer.release());
+ }
+
+ // Select the target bitmap into the memory dc and copy the rect from desktop
+ // to memory.
+ DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
+ queue_.current_frame()->GetUnderlyingFrame());
+ HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
+ if (previous_object != NULL) {
+ BitBlt(memory_dc_,
+ 0, 0, screen_rect.width(), screen_rect.height(),
+ desktop_dc_,
+ screen_rect.left(), screen_rect.top(),
+ SRCCOPY | CAPTUREBLT);
+
+ // Select back the previously selected object to that the device contect
+ // could be destroyed independently of the bitmap if needed.
+ SelectObject(memory_dc_, previous_object);
+ }
+ return true;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h b/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
new file mode 100644
index 0000000000..202b9aaa87
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
+
+#include "webrtc/modules/desktop_capture/screen_capturer.h"
+
+#include <windows.h>
+
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
+#include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
+#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
+
+namespace webrtc {
+
+class Differ;
+
+// ScreenCapturerWinGdi captures 32bit RGB using GDI.
+//
+// ScreenCapturerWinGdi is double-buffered as required by ScreenCapturer.
+class ScreenCapturerWinGdi : public ScreenCapturer {
+ public:
+ explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options);
+ virtual ~ScreenCapturerWinGdi();
+
+ // Overridden from ScreenCapturer:
+ void Start(Callback* callback) override;
+ void Capture(const DesktopRegion& region) override;
+ bool GetScreenList(ScreenList* screens) override;
+ bool SelectScreen(ScreenId id) override;
+
+ private:
+ typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT);
+
+ // Make sure that the device contexts match the screen configuration.
+ void PrepareCaptureResources();
+
+ // Captures the current screen contents into the current buffer. Returns true
+ // if succeeded.
+ bool CaptureImage();
+
+ // Capture the current cursor shape.
+ void CaptureCursor();
+
+ Callback* callback_;
+ ScreenId current_screen_id_;
+ std::wstring current_device_key_;
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ ScreenCapturerHelper helper_;
+
+ ScopedThreadDesktop desktop_;
+
+ // GDI resources used for screen capture.
+ HDC desktop_dc_;
+ HDC memory_dc_;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue queue_;
+
+ // Rectangle describing the bounds of the desktop device context, relative to
+ // the primary display's top-left.
+ DesktopRect desktop_dc_rect_;
+
+ // Class to calculate the difference between two screen bitmaps.
+ rtc::scoped_ptr<Differ> differ_;
+
+ HMODULE dwmapi_library_;
+ DwmEnableCompositionFunc composition_func_;
+
+ // Used to suppress duplicate logging of SetThreadExecutionState errors.
+ bool set_thread_execution_state_failed_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinGdi);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc b/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
new file mode 100644
index 0000000000..db40478023
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
@@ -0,0 +1,449 @@
+/*
+ * 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 "webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h"
+
+#include <assert.h>
+
+#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
+#include "webrtc/modules/desktop_capture/desktop_frame.h"
+#include "webrtc/modules/desktop_capture/desktop_frame_win.h"
+#include "webrtc/modules/desktop_capture/desktop_region.h"
+#include "webrtc/modules/desktop_capture/differ.h"
+#include "webrtc/modules/desktop_capture/mouse_cursor.h"
+#include "webrtc/modules/desktop_capture/win/cursor.h"
+#include "webrtc/modules/desktop_capture/win/desktop.h"
+#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
+#include "webrtc/system_wrappers/include/logging.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+
+namespace webrtc {
+
+// kMagnifierWindowClass has to be "Magnifier" according to the Magnification
+// API. The other strings can be anything.
+static LPCTSTR kMagnifierHostClass = L"ScreenCapturerWinMagnifierHost";
+static LPCTSTR kHostWindowName = L"MagnifierHost";
+static LPCTSTR kMagnifierWindowClass = L"Magnifier";
+static LPCTSTR kMagnifierWindowName = L"MagnifierWindow";
+
+Atomic32 ScreenCapturerWinMagnifier::tls_index_(TLS_OUT_OF_INDEXES);
+
+ScreenCapturerWinMagnifier::ScreenCapturerWinMagnifier(
+ rtc::scoped_ptr<ScreenCapturer> fallback_capturer)
+ : fallback_capturer_(fallback_capturer.Pass()),
+ fallback_capturer_started_(false),
+ callback_(NULL),
+ current_screen_id_(kFullDesktopScreenId),
+ excluded_window_(NULL),
+ set_thread_execution_state_failed_(false),
+ desktop_dc_(NULL),
+ mag_lib_handle_(NULL),
+ mag_initialize_func_(NULL),
+ mag_uninitialize_func_(NULL),
+ set_window_source_func_(NULL),
+ set_window_filter_list_func_(NULL),
+ set_image_scaling_callback_func_(NULL),
+ host_window_(NULL),
+ magnifier_window_(NULL),
+ magnifier_initialized_(false),
+ magnifier_capture_succeeded_(true) {
+}
+
+ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() {
+ // DestroyWindow must be called before MagUninitialize. magnifier_window_ is
+ // destroyed automatically when host_window_ is destroyed.
+ if (host_window_)
+ DestroyWindow(host_window_);
+
+ if (magnifier_initialized_)
+ mag_uninitialize_func_();
+
+ if (mag_lib_handle_)
+ FreeLibrary(mag_lib_handle_);
+
+ if (desktop_dc_)
+ ReleaseDC(NULL, desktop_dc_);
+}
+
+void ScreenCapturerWinMagnifier::Start(Callback* callback) {
+ assert(!callback_);
+ assert(callback);
+ callback_ = callback;
+
+ InitializeMagnifier();
+}
+
+void ScreenCapturerWinMagnifier::Capture(const DesktopRegion& region) {
+ TickTime capture_start_time = TickTime::Now();
+
+ queue_.MoveToNextFrame();
+
+ // Request that the system not power-down the system, or the display hardware.
+ if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
+ if (!set_thread_execution_state_failed_) {
+ set_thread_execution_state_failed_ = true;
+ LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
+ << GetLastError();
+ }
+ }
+ // Switch to the desktop receiving user input if different from the current
+ // one.
+ rtc::scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
+ if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
+ // Release GDI resources otherwise SetThreadDesktop will fail.
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = NULL;
+ }
+ // If SetThreadDesktop() fails, the thread is still assigned a desktop.
+ // So we can continue capture screen bits, just from the wrong desktop.
+ desktop_.SetThreadDesktop(input_desktop.release());
+ }
+
+ bool succeeded = false;
+
+ // Do not try to use the magnifier if it failed before and in multi-screen
+ // setup (where the API crashes sometimes).
+ if (magnifier_initialized_ && (GetSystemMetrics(SM_CMONITORS) == 1) &&
+ magnifier_capture_succeeded_) {
+ DesktopRect rect = GetScreenRect(current_screen_id_, current_device_key_);
+ CreateCurrentFrameIfNecessary(rect.size());
+
+ // CaptureImage may fail in some situations, e.g. windows8 metro mode.
+ succeeded = CaptureImage(rect);
+ }
+
+ // Defer to the fallback capturer if magnifier capturer did not work.
+ if (!succeeded) {
+ LOG_F(LS_WARNING) << "Switching to the fallback screen capturer.";
+ StartFallbackCapturer();
+ fallback_capturer_->Capture(region);
+ return;
+ }
+
+ const DesktopFrame* current_frame = queue_.current_frame();
+ const DesktopFrame* last_frame = queue_.previous_frame();
+ if (last_frame && last_frame->size().equals(current_frame->size())) {
+ // Make sure the differencer is set up correctly for these previous and
+ // current screens.
+ if (!differ_.get() || (differ_->width() != current_frame->size().width()) ||
+ (differ_->height() != current_frame->size().height()) ||
+ (differ_->bytes_per_row() != current_frame->stride())) {
+ differ_.reset(new Differ(current_frame->size().width(),
+ current_frame->size().height(),
+ DesktopFrame::kBytesPerPixel,
+ current_frame->stride()));
+ }
+
+ // Calculate difference between the two last captured frames.
+ DesktopRegion region;
+ differ_->CalcDirtyRegion(
+ last_frame->data(), current_frame->data(), &region);
+ helper_.InvalidateRegion(region);
+ } else {
+ // No previous frame is available, or the screen is resized. Invalidate the
+ // whole screen.
+ helper_.InvalidateScreen(current_frame->size());
+ }
+
+ helper_.set_size_most_recent(current_frame->size());
+
+ // Emit the current frame.
+ DesktopFrame* frame = queue_.current_frame()->Share();
+ frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
+ GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
+ frame->mutable_updated_region()->Clear();
+ helper_.TakeInvalidRegion(frame->mutable_updated_region());
+ frame->set_capture_time_ms(
+ (TickTime::Now() - capture_start_time).Milliseconds());
+ callback_->OnCaptureCompleted(frame);
+}
+
+bool ScreenCapturerWinMagnifier::GetScreenList(ScreenList* screens) {
+ return webrtc::GetScreenList(screens);
+}
+
+bool ScreenCapturerWinMagnifier::SelectScreen(ScreenId id) {
+ bool valid = IsScreenValid(id, &current_device_key_);
+
+ // Set current_screen_id_ even if the fallback capturer is being used, so we
+ // can switch back to the magnifier when possible.
+ if (valid)
+ current_screen_id_ = id;
+
+ if (fallback_capturer_started_)
+ fallback_capturer_->SelectScreen(id);
+
+ return valid;
+}
+
+void ScreenCapturerWinMagnifier::SetExcludedWindow(WindowId excluded_window) {
+ excluded_window_ = (HWND)excluded_window;
+ if (excluded_window_ && magnifier_initialized_) {
+ set_window_filter_list_func_(
+ magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_);
+ }
+}
+
+bool ScreenCapturerWinMagnifier::CaptureImage(const DesktopRect& rect) {
+ assert(magnifier_initialized_);
+
+ // Set the magnifier control to cover the captured rect. The content of the
+ // magnifier control will be the captured image.
+ BOOL result = SetWindowPos(magnifier_window_,
+ NULL,
+ rect.left(), rect.top(),
+ rect.width(), rect.height(),
+ 0);
+ if (!result) {
+ LOG_F(LS_WARNING) << "Failed to call SetWindowPos: " << GetLastError()
+ << ". Rect = {" << rect.left() << ", " << rect.top()
+ << ", " << rect.right() << ", " << rect.bottom() << "}";
+ return false;
+ }
+
+ magnifier_capture_succeeded_ = false;
+
+ RECT native_rect = {rect.left(), rect.top(), rect.right(), rect.bottom()};
+
+ // OnCaptured will be called via OnMagImageScalingCallback and fill in the
+ // frame before set_window_source_func_ returns.
+ result = set_window_source_func_(magnifier_window_, native_rect);
+
+ if (!result) {
+ LOG_F(LS_WARNING) << "Failed to call MagSetWindowSource: " << GetLastError()
+ << ". Rect = {" << rect.left() << ", " << rect.top()
+ << ", " << rect.right() << ", " << rect.bottom() << "}";
+ return false;
+ }
+
+ return magnifier_capture_succeeded_;
+}
+
+BOOL ScreenCapturerWinMagnifier::OnMagImageScalingCallback(
+ HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty) {
+ assert(tls_index_.Value() != TLS_OUT_OF_INDEXES);
+
+ ScreenCapturerWinMagnifier* owner =
+ reinterpret_cast<ScreenCapturerWinMagnifier*>(
+ TlsGetValue(tls_index_.Value()));
+
+ owner->OnCaptured(srcdata, srcheader);
+
+ return TRUE;
+}
+
+bool ScreenCapturerWinMagnifier::InitializeMagnifier() {
+ assert(!magnifier_initialized_);
+
+ desktop_dc_ = GetDC(NULL);
+
+ mag_lib_handle_ = LoadLibrary(L"Magnification.dll");
+ if (!mag_lib_handle_)
+ return false;
+
+ // Initialize Magnification API function pointers.
+ mag_initialize_func_ = reinterpret_cast<MagInitializeFunc>(
+ GetProcAddress(mag_lib_handle_, "MagInitialize"));
+ mag_uninitialize_func_ = reinterpret_cast<MagUninitializeFunc>(
+ GetProcAddress(mag_lib_handle_, "MagUninitialize"));
+ set_window_source_func_ = reinterpret_cast<MagSetWindowSourceFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetWindowSource"));
+ set_window_filter_list_func_ = reinterpret_cast<MagSetWindowFilterListFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetWindowFilterList"));
+ set_image_scaling_callback_func_ =
+ reinterpret_cast<MagSetImageScalingCallbackFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetImageScalingCallback"));
+
+ if (!mag_initialize_func_ || !mag_uninitialize_func_ ||
+ !set_window_source_func_ || !set_window_filter_list_func_ ||
+ !set_image_scaling_callback_func_) {
+ LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ << "library functions missing.";
+ return false;
+ }
+
+ BOOL result = mag_initialize_func_();
+ if (!result) {
+ LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ << "error from MagInitialize " << GetLastError();
+ return false;
+ }
+
+ HMODULE hInstance = NULL;
+ result = GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<char*>(&DefWindowProc),
+ &hInstance);
+ if (!result) {
+ mag_uninitialize_func_();
+ LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ << "error from GetModulehandleExA " << GetLastError();
+ return false;
+ }
+
+ // Register the host window class. See the MSDN documentation of the
+ // Magnification API for more infomation.
+ WNDCLASSEX wcex = {};
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.lpfnWndProc = &DefWindowProc;
+ wcex.hInstance = hInstance;
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.lpszClassName = kMagnifierHostClass;
+
+ // Ignore the error which may happen when the class is already registered.
+ RegisterClassEx(&wcex);
+
+ // Create the host window.
+ host_window_ = CreateWindowEx(WS_EX_LAYERED,
+ kMagnifierHostClass,
+ kHostWindowName,
+ 0,
+ 0, 0, 0, 0,
+ NULL,
+ NULL,
+ hInstance,
+ NULL);
+ if (!host_window_) {
+ mag_uninitialize_func_();
+ LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ << "error from creating host window " << GetLastError();
+ return false;
+ }
+
+ // Create the magnifier control.
+ magnifier_window_ = CreateWindow(kMagnifierWindowClass,
+ kMagnifierWindowName,
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, 0, 0,
+ host_window_,
+ NULL,
+ hInstance,
+ NULL);
+ if (!magnifier_window_) {
+ mag_uninitialize_func_();
+ LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ << "error from creating magnifier window "
+ << GetLastError();
+ return false;
+ }
+
+ // Hide the host window.
+ ShowWindow(host_window_, SW_HIDE);
+
+ // Set the scaling callback to receive captured image.
+ result = set_image_scaling_callback_func_(
+ magnifier_window_,
+ &ScreenCapturerWinMagnifier::OnMagImageScalingCallback);
+ if (!result) {
+ mag_uninitialize_func_();
+ LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ << "error from MagSetImageScalingCallback "
+ << GetLastError();
+ return false;
+ }
+
+ if (excluded_window_) {
+ result = set_window_filter_list_func_(
+ magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_);
+ if (!result) {
+ mag_uninitialize_func_();
+ LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ << "error from MagSetWindowFilterList "
+ << GetLastError();
+ return false;
+ }
+ }
+
+ if (tls_index_.Value() == TLS_OUT_OF_INDEXES) {
+ // More than one threads may get here at the same time, but only one will
+ // write to tls_index_ using CompareExchange.
+ DWORD new_tls_index = TlsAlloc();
+ if (!tls_index_.CompareExchange(new_tls_index, TLS_OUT_OF_INDEXES))
+ TlsFree(new_tls_index);
+ }
+
+ assert(tls_index_.Value() != TLS_OUT_OF_INDEXES);
+ TlsSetValue(tls_index_.Value(), this);
+
+ magnifier_initialized_ = true;
+ return true;
+}
+
+void ScreenCapturerWinMagnifier::OnCaptured(void* data,
+ const MAGIMAGEHEADER& header) {
+ DesktopFrame* current_frame = queue_.current_frame();
+
+ // Verify the format.
+ // TODO(jiayl): support capturing sources with pixel formats other than RGBA.
+ int captured_bytes_per_pixel = header.cbSize / header.width / header.height;
+ if (header.format != GUID_WICPixelFormat32bppRGBA ||
+ header.width != static_cast<UINT>(current_frame->size().width()) ||
+ header.height != static_cast<UINT>(current_frame->size().height()) ||
+ header.stride != static_cast<UINT>(current_frame->stride()) ||
+ captured_bytes_per_pixel != DesktopFrame::kBytesPerPixel) {
+ LOG_F(LS_WARNING) << "Output format does not match the captured format: "
+ << "width = " << header.width << ", "
+ << "height = " << header.height << ", "
+ << "stride = " << header.stride << ", "
+ << "bpp = " << captured_bytes_per_pixel << ", "
+ << "pixel format RGBA ? "
+ << (header.format == GUID_WICPixelFormat32bppRGBA) << ".";
+ return;
+ }
+
+ // Copy the data into the frame.
+ current_frame->CopyPixelsFrom(
+ reinterpret_cast<uint8_t*>(data),
+ header.stride,
+ DesktopRect::MakeXYWH(0, 0, header.width, header.height));
+
+ magnifier_capture_succeeded_ = true;
+}
+
+void ScreenCapturerWinMagnifier::CreateCurrentFrameIfNecessary(
+ const DesktopSize& size) {
+ // If the current buffer is from an older generation then allocate a new one.
+ // 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_.current_frame()->size().equals(size)) {
+ size_t buffer_size =
+ size.width() * size.height() * DesktopFrame::kBytesPerPixel;
+ SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size);
+
+ rtc::scoped_ptr<DesktopFrame> buffer;
+ if (shared_memory) {
+ buffer.reset(new SharedMemoryDesktopFrame(
+ size, size.width() * DesktopFrame::kBytesPerPixel, shared_memory));
+ } else {
+ buffer.reset(new BasicDesktopFrame(size));
+ }
+ queue_.ReplaceCurrentFrame(buffer.release());
+ }
+}
+
+void ScreenCapturerWinMagnifier::StartFallbackCapturer() {
+ assert(fallback_capturer_);
+ if (!fallback_capturer_started_) {
+ fallback_capturer_started_ = true;
+
+ fallback_capturer_->Start(callback_);
+ fallback_capturer_->SelectScreen(current_screen_id_);
+ }
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h b/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
new file mode 100644
index 0000000000..f084e25442
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
+#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
+
+#include <windows.h>
+#include <magnification.h>
+#include <wincodec.h>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
+#include "webrtc/modules/desktop_capture/screen_capturer.h"
+#include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
+#include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
+#include "webrtc/system_wrappers/include/atomic32.h"
+
+namespace webrtc {
+
+class DesktopFrame;
+class DesktopRect;
+class Differ;
+
+// Captures the screen using the Magnification API to support window exclusion.
+// Each capturer must run on a dedicated thread because it uses thread local
+// storage for redirecting the library callback. Also the thread must have a UI
+// message loop to handle the window messages for the magnifier window.
+class ScreenCapturerWinMagnifier : public ScreenCapturer {
+ public:
+ // |fallback_capturer| will be used to capture the screen if a non-primary
+ // screen is being captured, or the OS does not support Magnification API, or
+ // the magnifier capturer fails (e.g. in Windows8 Metro mode).
+ explicit ScreenCapturerWinMagnifier(
+ rtc::scoped_ptr<ScreenCapturer> fallback_capturer);
+ virtual ~ScreenCapturerWinMagnifier();
+
+ // Overridden from ScreenCapturer:
+ void Start(Callback* callback) override;
+ void Capture(const DesktopRegion& region) override;
+ bool GetScreenList(ScreenList* screens) override;
+ bool SelectScreen(ScreenId id) override;
+ void SetExcludedWindow(WindowId window) override;
+
+ private:
+ typedef BOOL(WINAPI* MagImageScalingCallback)(HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+ typedef BOOL(WINAPI* MagInitializeFunc)(void);
+ typedef BOOL(WINAPI* MagUninitializeFunc)(void);
+ typedef BOOL(WINAPI* MagSetWindowSourceFunc)(HWND hwnd, RECT rect);
+ typedef BOOL(WINAPI* MagSetWindowFilterListFunc)(HWND hwnd,
+ DWORD dwFilterMode,
+ int count,
+ HWND* pHWND);
+ typedef BOOL(WINAPI* MagSetImageScalingCallbackFunc)(
+ HWND hwnd,
+ MagImageScalingCallback callback);
+
+ static BOOL WINAPI OnMagImageScalingCallback(HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+
+ // Captures the screen within |rect| in the desktop coordinates. Returns true
+ // if succeeded.
+ // It can only capture the primary screen for now. The magnification library
+ // crashes under some screen configurations (e.g. secondary screen on top of
+ // primary screen) if it tries to capture a non-primary screen. The caller
+ // must make sure not calling it on non-primary screens.
+ bool CaptureImage(const DesktopRect& rect);
+
+ // Helper method for setting up the magnifier control. Returns true if
+ // succeeded.
+ bool InitializeMagnifier();
+
+ // Called by OnMagImageScalingCallback to output captured data.
+ void OnCaptured(void* data, const MAGIMAGEHEADER& header);
+
+ // Makes sure the current frame exists and matches |size|.
+ void CreateCurrentFrameIfNecessary(const DesktopSize& size);
+
+ // Start the fallback capturer and select the screen.
+ void StartFallbackCapturer();
+
+ static Atomic32 tls_index_;
+
+ rtc::scoped_ptr<ScreenCapturer> fallback_capturer_;
+ bool fallback_capturer_started_;
+ Callback* callback_;
+ ScreenId current_screen_id_;
+ std::wstring current_device_key_;
+ HWND excluded_window_;
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ ScreenCapturerHelper helper_;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue queue_;
+
+ // Class to calculate the difference between two screen bitmaps.
+ rtc::scoped_ptr<Differ> differ_;
+
+ // Used to suppress duplicate logging of SetThreadExecutionState errors.
+ bool set_thread_execution_state_failed_;
+
+ ScopedThreadDesktop desktop_;
+
+ // Used for getting the screen dpi.
+ HDC desktop_dc_;
+
+ HMODULE mag_lib_handle_;
+ MagInitializeFunc mag_initialize_func_;
+ MagUninitializeFunc mag_uninitialize_func_;
+ MagSetWindowSourceFunc set_window_source_func_;
+ MagSetWindowFilterListFunc set_window_filter_list_func_;
+ MagSetImageScalingCallbackFunc set_image_scaling_callback_func_;
+
+ // The hidden window hosting the magnifier control.
+ HWND host_window_;
+ // The magnifier control that captures the screen.
+ HWND magnifier_window_;
+
+ // True if the magnifier control has been successfully initialized.
+ bool magnifier_initialized_;
+
+ // True if the last OnMagImageScalingCallback was called and handled
+ // successfully. Reset at the beginning of each CaptureImage call.
+ bool magnifier_capture_succeeded_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinMagnifier);
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
diff --git a/webrtc/modules/desktop_capture/win/window_capture_utils.cc b/webrtc/modules/desktop_capture/win/window_capture_utils.cc
new file mode 100644
index 0000000000..83922ea7f8
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/window_capture_utils.cc
@@ -0,0 +1,69 @@
+/*
+ * 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 "webrtc/modules/desktop_capture/win/window_capture_utils.h"
+
+namespace webrtc {
+
+bool
+GetCroppedWindowRect(HWND window,
+ DesktopRect* cropped_rect,
+ DesktopRect* original_rect) {
+ RECT rect;
+ if (!GetWindowRect(window, &rect)) {
+ return false;
+ }
+ WINDOWPLACEMENT window_placement;
+ window_placement.length = sizeof(window_placement);
+ if (!GetWindowPlacement(window, &window_placement)) {
+ return false;
+ }
+
+ *original_rect = DesktopRect::MakeLTRB(
+ rect.left, rect.top, rect.right, rect.bottom);
+
+ if (window_placement.showCmd == SW_SHOWMAXIMIZED) {
+ DesktopSize border = DesktopSize(GetSystemMetrics(SM_CXSIZEFRAME),
+ GetSystemMetrics(SM_CYSIZEFRAME));
+ *cropped_rect = DesktopRect::MakeLTRB(
+ rect.left + border.width(),
+ rect.top,
+ rect.right - border.width(),
+ rect.bottom - border.height());
+ } else {
+ *cropped_rect = *original_rect;
+ }
+ return true;
+}
+
+AeroChecker::AeroChecker() : dwmapi_library_(nullptr), func_(nullptr) {
+ // Try to load dwmapi.dll dynamically since it is not available on XP.
+ dwmapi_library_ = LoadLibrary(L"dwmapi.dll");
+ if (dwmapi_library_) {
+ func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>(
+ GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
+ }
+}
+
+AeroChecker::~AeroChecker() {
+ if (dwmapi_library_) {
+ FreeLibrary(dwmapi_library_);
+ }
+}
+
+bool AeroChecker::IsAeroEnabled() {
+ BOOL result = FALSE;
+ if (func_) {
+ func_(&result);
+ }
+ return result != FALSE;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/desktop_capture/win/window_capture_utils.h b/webrtc/modules/desktop_capture/win/window_capture_utils.h
new file mode 100644
index 0000000000..7c80490f60
--- /dev/null
+++ b/webrtc/modules/desktop_capture/win/window_capture_utils.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <windows.h>
+
+#include "webrtc/modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// Output the window rect, with the left/right/bottom frame border cropped if
+// the window is maximized. |cropped_rect| is the cropped rect relative to the
+// desktop. |original_rect| is the original rect returned from GetWindowRect.
+// Returns true if all API calls succeeded.
+bool GetCroppedWindowRect(HWND window,
+ DesktopRect* cropped_rect,
+ DesktopRect* original_rect);
+
+typedef HRESULT (WINAPI *DwmIsCompositionEnabledFunc)(BOOL* enabled);
+class AeroChecker {
+ public:
+ AeroChecker();
+ ~AeroChecker();
+
+ bool IsAeroEnabled();
+
+ private:
+ HMODULE dwmapi_library_;
+ DwmIsCompositionEnabledFunc func_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(AeroChecker);
+};
+
+} // namespace webrtc