feat: add support for DesktopCapturerSource.appIcon

Useful to get the icon of the application owning the source.
Only available for sources of type window, i.e. not for screen.

https://github.com/electron/electron/issues/14845
This commit is contained in:
Julien Isorce 2018-12-03 22:42:49 -08:00 committed by Cheng Zhao
parent 77f73830e8
commit 1f55f1635f
7 changed files with 48 additions and 15 deletions

View file

@ -14,6 +14,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "chrome/browser/media/webrtc/desktop_media_list.h" #include "chrome/browser/media/webrtc/desktop_media_list.h"
#include "chrome/browser/media/webrtc/window_icon_util.h"
#include "content/public/browser/desktop_capture.h" #include "content/public/browser/desktop_capture.h"
#include "native_mate/dictionary.h" #include "native_mate/dictionary.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
@ -42,6 +43,12 @@ struct Converter<atom::api::DesktopCapturer::Source> {
atom::api::NativeImage::Create( atom::api::NativeImage::Create(
isolate, gfx::Image(source.media_list_source.thumbnail))); isolate, gfx::Image(source.media_list_source.thumbnail)));
dict.Set("display_id", source.display_id); dict.Set("display_id", source.display_id);
if (source.fetch_icon) {
dict.Set(
"appIcon",
atom::api::NativeImage::Create(
isolate, gfx::Image(GetWindowIcon(source.media_list_source.id))));
}
return ConvertToV8(isolate, dict); return ConvertToV8(isolate, dict);
} }
}; };
@ -60,7 +67,9 @@ DesktopCapturer::~DesktopCapturer() {}
void DesktopCapturer::StartHandling(bool capture_window, void DesktopCapturer::StartHandling(bool capture_window,
bool capture_screen, bool capture_screen,
const gfx::Size& thumbnail_size) { const gfx::Size& thumbnail_size,
bool fetch_window_icons) {
fetch_window_icons_ = fetch_window_icons;
#if defined(OS_WIN) #if defined(OS_WIN)
if (content::desktop_capture::CreateDesktopCaptureOptions() if (content::desktop_capture::CreateDesktopCaptureOptions()
.allow_directx_capturer()) { .allow_directx_capturer()) {
@ -134,8 +143,8 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
capture_window_ = false; capture_window_ = false;
const auto& media_list_sources = list->GetSources(); const auto& media_list_sources = list->GetSources();
for (const auto& media_list_source : media_list_sources) { for (const auto& media_list_source : media_list_sources) {
window_sources.emplace_back( window_sources.emplace_back(DesktopCapturer::Source{
DesktopCapturer::Source{media_list_source, std::string()}); media_list_source, std::string(), fetch_window_icons_});
} }
std::move(window_sources.begin(), window_sources.end(), std::move(window_sources.begin(), window_sources.end(),
std::back_inserter(captured_sources_)); std::back_inserter(captured_sources_));
@ -187,7 +196,7 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
} }
if (!capture_window_ && !capture_screen_) if (!capture_window_ && !capture_screen_)
Emit("finished", captured_sources_); Emit("finished", captured_sources_, fetch_window_icons_);
} }
// static // static

View file

@ -25,6 +25,9 @@ class DesktopCapturer : public mate::EventEmitter<DesktopCapturer>,
DesktopMediaList::Source media_list_source; DesktopMediaList::Source media_list_source;
// Will be an empty string if not available. // Will be an empty string if not available.
std::string display_id; std::string display_id;
// Whether or not this source should provide an icon.
bool fetch_icon = false;
}; };
static mate::Handle<DesktopCapturer> Create(v8::Isolate* isolate); static mate::Handle<DesktopCapturer> Create(v8::Isolate* isolate);
@ -34,7 +37,8 @@ class DesktopCapturer : public mate::EventEmitter<DesktopCapturer>,
void StartHandling(bool capture_window, void StartHandling(bool capture_window,
bool capture_screen, bool capture_screen,
const gfx::Size& thumbnail_size); const gfx::Size& thumbnail_size,
bool fetch_window_icons);
protected: protected:
explicit DesktopCapturer(v8::Isolate* isolate); explicit DesktopCapturer(v8::Isolate* isolate);
@ -59,6 +63,7 @@ class DesktopCapturer : public mate::EventEmitter<DesktopCapturer>,
std::vector<DesktopCapturer::Source> captured_sources_; std::vector<DesktopCapturer::Source> captured_sources_;
bool capture_window_ = false; bool capture_window_ = false;
bool capture_screen_ = false; bool capture_screen_ = false;
bool fetch_window_icons_ = false;
#if defined(OS_WIN) #if defined(OS_WIN)
bool using_directx_capturer_ = false; bool using_directx_capturer_ = false;
#endif // defined(OS_WIN) #endif // defined(OS_WIN)

View file

@ -72,6 +72,7 @@ static_library("chrome") {
"//chrome/browser/media/webrtc/desktop_media_list_observer.h", "//chrome/browser/media/webrtc/desktop_media_list_observer.h",
"//chrome/browser/media/webrtc/native_desktop_media_list.cc", "//chrome/browser/media/webrtc/native_desktop_media_list.cc",
"//chrome/browser/media/webrtc/native_desktop_media_list.h", "//chrome/browser/media/webrtc/native_desktop_media_list.h",
"//chrome/browser/media/webrtc/window_icon_util.h",
] ]
deps += [ "//ui/snapshot" ] deps += [ "//ui/snapshot" ]
} }
@ -93,6 +94,7 @@ static_library("chrome") {
if (is_mac) { if (is_mac) {
sources += [ sources += [
"//chrome/browser/media/webrtc/window_icon_util_mac.mm",
"//chrome/browser/ui/cocoa/color_chooser_mac.h", "//chrome/browser/ui/cocoa/color_chooser_mac.h",
"//chrome/browser/ui/cocoa/color_chooser_mac.mm", "//chrome/browser/ui/cocoa/color_chooser_mac.mm",
] ]
@ -100,6 +102,7 @@ static_library("chrome") {
if (is_win) { if (is_win) {
sources += [ sources += [
"//chrome/browser/media/webrtc/window_icon_util_win.cc",
"//chrome/browser/ui/views/color_chooser_dialog.cc", "//chrome/browser/ui/views/color_chooser_dialog.cc",
"//chrome/browser/ui/views/color_chooser_dialog.h", "//chrome/browser/ui/views/color_chooser_dialog.h",
"//chrome/browser/ui/views/color_chooser_win.cc", "//chrome/browser/ui/views/color_chooser_win.cc",
@ -126,7 +129,10 @@ static_library("chrome") {
] ]
if (is_linux) { if (is_linux) {
sources += [ "//chrome/browser/speech/tts_linux.cc" ] sources += [
"//chrome/browser/media/webrtc/window_icon_util_x11.cc",
"//chrome/browser/speech/tts_linux.cc",
]
deps += [ "//third_party/speech-dispatcher" ] deps += [ "//third_party/speech-dispatcher" ]
} }
} }

View file

@ -84,6 +84,9 @@ The `desktopCapturer` module has the following methods:
to be captured, available types are `screen` and `window`. to be captured, available types are `screen` and `window`.
* `thumbnailSize` [Size](structures/size.md) (optional) - The size that the media source thumbnail * `thumbnailSize` [Size](structures/size.md) (optional) - The size that the media source thumbnail
should be scaled to. Default is `150` x `150`. should be scaled to. Default is `150` x `150`.
* `fetchWindowIcons` Boolean (optional) - Set to true to enable fetching window icons. The default
value is false. When false the appIcon property of the sources return null. Same if a source has
the type screen.
* `callback` Function * `callback` Function
* `error` Error * `error` Error
* `sources` [DesktopCapturerSource[]](structures/desktop-capturer-source.md) * `sources` [DesktopCapturerSource[]](structures/desktop-capturer-source.md)

View file

@ -17,3 +17,7 @@
On some platforms, this is equivalent to the `XX` portion of the `id` field On some platforms, this is equivalent to the `XX` portion of the `id` field
above and on others it will differ. It will be an empty string if not above and on others it will differ. It will be an empty string if not
available. available.
* `appIcon` [NativeImage](../native-image.md) - An icon image of the
application that owns the window or null if the source has a type screen.
The size of the icon is not known in advance and depends on what the
the application provides.

View file

@ -11,19 +11,20 @@ let requestsQueue = []
const electronSources = 'ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES' const electronSources = 'ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES'
const capturerResult = (id) => `ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}` const capturerResult = (id) => `ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`
ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize, id) => { ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize, fetchWindowIcons, id) => {
const request = { const request = {
id, id,
options: { options: {
captureWindow, captureWindow,
captureScreen, captureScreen,
thumbnailSize thumbnailSize,
fetchWindowIcons
}, },
webContents: event.sender webContents: event.sender
} }
requestsQueue.push(request) requestsQueue.push(request)
if (requestsQueue.length === 1) { if (requestsQueue.length === 1) {
desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize) desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons)
} }
// If the WebContents is destroyed before receiving result, just remove the // If the WebContents is destroyed before receiving result, just remove the
@ -33,7 +34,7 @@ ipcMain.on(electronSources, (event, captureWindow, captureScreen, thumbnailSize,
}) })
}) })
desktopCapturer.emit = (event, name, sources) => { desktopCapturer.emit = (event, name, sources, fetchWindowIcons) => {
// Receiving sources result from main process, now send them back to renderer. // Receiving sources result from main process, now send them back to renderer.
const handledRequest = requestsQueue.shift() const handledRequest = requestsQueue.shift()
const handledWebContents = handledRequest.webContents const handledWebContents = handledRequest.webContents
@ -44,7 +45,8 @@ desktopCapturer.emit = (event, name, sources) => {
id: source.id, id: source.id,
name: source.name, name: source.name,
thumbnail: source.thumbnail.toDataURL(), thumbnail: source.thumbnail.toDataURL(),
display_id: source.display_id display_id: source.display_id,
appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null
} }
}) })
@ -67,7 +69,7 @@ desktopCapturer.emit = (event, name, sources) => {
// If the requestsQueue is not empty, start a new request handling. // If the requestsQueue is not empty, start a new request handling.
if (requestsQueue.length > 0) { if (requestsQueue.length > 0) {
const { captureWindow, captureScreen, thumbnailSize } = requestsQueue[0].options const { captureWindow, captureScreen, thumbnailSize, fetchWindowIcons } = requestsQueue[0].options
return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize) return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons)
} }
} }

View file

@ -28,9 +28,12 @@ exports.getSources = function (options, callback) {
height: 150 height: 150
} }
} }
if (options.fetchWindowIcons == null) {
options.fetchWindowIcons = false
}
const id = incrementId() const id = incrementId()
ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id) ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, options.fetchWindowIcons, id)
return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => { return ipcRenderer.once(`ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_${id}`, (event, sources) => {
callback(null, (() => { callback(null, (() => {
const results = [] const results = []
@ -39,7 +42,8 @@ exports.getSources = function (options, callback) {
id: source.id, id: source.id,
name: source.name, name: source.name,
thumbnail: nativeImage.createFromDataURL(source.thumbnail), thumbnail: nativeImage.createFromDataURL(source.thumbnail),
display_id: source.display_id display_id: source.display_id,
appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null
}) })
}) })
return results return results