Compare commits

...

1 Commits

Author SHA1 Message Date
Samuel Attard
36b6c2272b feat: change DesktopCapturerSource.display_id to displayId
This is an API smell that's annoyed me for a while, this is a backwards-compatible
migration from display_id to displayId.  Accessing the old property will emit a deprecation
warning, it's also flagged deprecated in typescript though I'm open to changing that to be a full removal.
2022-09-20 00:13:07 -07:00
6 changed files with 47 additions and 10 deletions

View File

@@ -15,11 +15,12 @@
`thumbnailSize` specified in the `options` passed to `thumbnailSize` specified in the `options` passed to
`desktopCapturer.getSources`. The actual size depends on the scale of the `desktopCapturer.getSources`. The actual size depends on the scale of the
screen or window. screen or window.
* `display_id` string - A unique identifier that will correspond to the `id` of * `displayId` string - A unique identifier that will correspond to the `id` of
the matching [Display](display.md) returned by the [Screen API](../screen.md). the matching [Display](display.md) returned by the [Screen API](../screen.md).
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.
* `display_id` string _Deprecated_ - Deprecated accessor for `displayId`.
* `appIcon` [NativeImage](../native-image.md) - An icon image of the * `appIcon` [NativeImage](../native-image.md) - An icon image of the
application that owns the window or null if the source has a type screen. 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 size of the icon is not known in advance and depends on what

View File

@@ -44,7 +44,11 @@ struct Converter<electron::api::DesktopCapturer::Source> {
dict.Set("thumbnail", dict.Set("thumbnail",
electron::api::NativeImage::Create( electron::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("displayId", source.display_id);
dict.SetDeprecated(
"display_id", source.display_id,
"The display_id property on the DesktopCapturerSource object is "
"deprecated, please us the displayId property instead");
if (source.fetch_icon) { if (source.fetch_icon) {
dict.Set( dict.Set(
"appIcon", "appIcon",

View File

@@ -9,13 +9,29 @@
#include <utility> #include <utility>
#include "gin/dictionary.h" #include "gin/dictionary.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/std_converter.h" #include "shell/common/gin_converters/std_converter.h"
#include "shell/common/gin_helper/accessor.h" #include "shell/common/gin_helper/accessor.h"
#include "shell/common/gin_helper/function_template.h" #include "shell/common/gin_helper/function_template.h"
#include "shell/common/node_includes.h"
#include "shell/common/process_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/abseil-cpp/absl/types/optional.h"
namespace gin_helper { namespace gin_helper {
namespace {
template <typename V>
v8::Local<v8::Value> DeprecatedValueProvider(const std::string& message,
v8::Isolate* isolate,
const V& value) {
electron::EmitWarning(node::Environment::GetCurrent(isolate), message,
"electron");
return gin::ConvertToV8(isolate, value);
}
} // namespace
// Adds a few more extends methods to gin::Dictionary. // Adds a few more extends methods to gin::Dictionary.
// //
// Note that as the destructor of gin::Dictionary is not virtual, and we want to // Note that as the destructor of gin::Dictionary is not virtual, and we want to
@@ -110,6 +126,22 @@ class Dictionary : public gin::Dictionary {
.ToChecked(); .ToChecked();
} }
template <typename K, typename V>
bool SetDeprecated(const K& key,
const V& val,
const std::string& message,
v8::PropertyAttribute attribute = v8::None) {
auto context = isolate()->GetCurrentContext();
v8::Local<v8::Value> getter = gin::ConvertToV8(
isolate(), base::BindRepeating(&DeprecatedValueProvider<V>, message,
isolate(), val));
v8::PropertyDescriptor prop_desc(getter, v8::Undefined(isolate()));
v8::Maybe<bool> maybe = GetHandle()->DefineProperty(
context, gin::StringToV8(isolate(), key), prop_desc);
return maybe.IsJust() && maybe.FromJust();
}
template <typename K, typename V> template <typename K, typename V>
bool SetGetter(const K& key, bool SetGetter(const K& key,
const V& val, const V& val,

View File

@@ -50,25 +50,25 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
}); });
// Linux doesn't return any window sources. // Linux doesn't return any window sources.
ifit(process.platform !== 'linux')('returns an empty display_id for window sources', async () => { ifit(process.platform !== 'linux')('returns an empty displayId for window sources', async () => {
const w = new BrowserWindow({ width: 200, height: 200 }); const w = new BrowserWindow({ width: 200, height: 200 });
await w.loadURL('about:blank'); await w.loadURL('about:blank');
const sources = await desktopCapturer.getSources({ types: ['window'] }); const sources = await desktopCapturer.getSources({ types: ['window'] });
w.destroy(); w.destroy();
expect(sources).to.be.an('array').that.is.not.empty(); expect(sources).to.be.an('array').that.is.not.empty();
for (const { display_id: displayId } of sources) { for (const { displayId } of sources) {
expect(displayId).to.be.a('string').and.be.empty(); expect(displayId).to.be.a('string').and.be.empty();
} }
}); });
ifit(process.platform !== 'linux')('returns display_ids matching the Screen API', async () => { ifit(process.platform !== 'linux')('returns displayIds matching the Screen API', async () => {
const displays = screen.getAllDisplays(); const displays = screen.getAllDisplays();
const sources = await desktopCapturer.getSources({ types: ['screen'] }); const sources = await desktopCapturer.getSources({ types: ['screen'] });
expect(sources).to.be.an('array').of.length(displays.length); expect(sources).to.be.an('array').of.length(displays.length);
for (let i = 0; i < sources.length; i++) { for (let i = 0; i < sources.length; i++) {
expect(sources[i].display_id).to.equal(displays[i].id.toString()); expect(sources[i].displayId).to.equal(displays[i].id.toString());
} }
}); });

View File

@@ -19,14 +19,14 @@ export const captureScreen = async (point: Electron.Point = { x: 0, y: 0 }): Pro
const DEBUG_CAPTURE = false; const DEBUG_CAPTURE = false;
if (DEBUG_CAPTURE) { if (DEBUG_CAPTURE) {
for (const source of sources) { for (const source of sources) {
await fs.promises.writeFile(path.join(fixtures, `screenshot_${source.display_id}.png`), source.thumbnail.toPNG()); await fs.promises.writeFile(path.join(fixtures, `screenshot_${source.displayId}.png`), source.thumbnail.toPNG());
} }
} }
const screenCapture = sources.find(source => source.display_id === `${display.id}`); const screenCapture = sources.find(source => source.displayId === `${display.id}`);
// Fails when HDR is enabled on Windows. // Fails when HDR is enabled on Windows.
// https://bugs.chromium.org/p/chromium/issues/detail?id=1247730 // https://bugs.chromium.org/p/chromium/issues/detail?id=1247730
if (!screenCapture) { if (!screenCapture) {
const displayIds = sources.map(source => source.display_id); const displayIds = sources.map(source => source.displayId);
throw new Error(`Unable to find screen capture for display '${display.id}'\n\tAvailable displays: ${displayIds.join(', ')}`); throw new Error(`Unable to find screen capture for display '${display.id}'\n\tAvailable displays: ${displayIds.join(', ')}`);
} }
return screenCapture.thumbnail; return screenCapture.thumbnail;

View File

@@ -204,7 +204,7 @@ declare namespace ElectronInternal {
id: string; id: string;
name: string; name: string;
thumbnail: Electron.NativeImage; thumbnail: Electron.NativeImage;
display_id: string; displayId: string;
appIcon: Electron.NativeImage | null; appIcon: Electron.NativeImage | null;
} }