From adaf1c3393672ea5e11d587c84b06de53f5813f4 Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Tue, 11 Feb 2025 16:35:11 -0800 Subject: [PATCH 1/6] Implement policy watcher for macOS something compiling and outputing a familiar looking key but more tidy this time prevent bad stuff file watcher (partly there) cc tidy up file watcher implement dispose implement mac NumberPolicy remove experiment dir tidy warnings minor edits revert package-lock.json remove pathsToWatch unsupported platform check value.value() is only supported after macOS 10.13 --- .vscode/c_cpp_properties.json | 14 +++++ .vscode/launch.json | 12 ++++ .vscode/settings.json | 14 ++++- binding.gyp | 2 + index.js | 5 +- src/PolicyWatcher.hh | 12 ++++ src/macos/NumberPolicy.cc | 42 +++++++++++++ src/macos/NumberPolicy.hh | 24 ++++++++ src/macos/PolicyWatcher.cc | 105 ++++++++++++++++++++++++++++++--- src/macos/PreferencesPolicy.hh | 62 +++++++++++++++++++ src/macos/StringPolicy.cc | 46 +++++++++++++++ src/macos/StringPolicy.hh | 23 ++++++++ src/main.cc | 12 ++-- 13 files changed, 360 insertions(+), 13 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/macos/NumberPolicy.cc create mode 100644 src/macos/NumberPolicy.hh create mode 100644 src/macos/PreferencesPolicy.hh create mode 100644 src/macos/StringPolicy.cc create mode 100644 src/macos/StringPolicy.hh diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 1709919..b265c98 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -13,7 +13,21 @@ "cStandard": "c17", "cppStandard": "c++17", "intelliSenseMode": "windows-msvc-x64" + }, + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/node_modules/node-addon-api", + "${default}" + ], + "defines": ["_DEBUG", "MACOS"], + "compilerPath": "/usr/bin/clang++", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "macos-clang-x64" } ], "version": 4 } + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..616d678 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/index.js" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 786b63a..33150c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -67,7 +67,19 @@ "xstring": "cpp", "xtr1common": "cpp", "xtree": "cpp", - "xutility": "cpp" + "xutility": "cpp", + "__bit_reference": "cpp", + "__locale": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__verbose_abort": "cpp", + "cstdarg": "cpp", + "cwctype": "cpp", + "execution": "cpp", + "string_view": "cpp", + "variant": "cpp", + "bitset": "cpp", + "print": "cpp" }, "git.branchProtection": ["main"], "git.branchProtectionPrompt": "alwaysCommitToNewBranch" diff --git a/binding.gyp b/binding.gyp index 6782357..e31f795 100644 --- a/binding.gyp +++ b/binding.gyp @@ -16,6 +16,8 @@ ['OS=="mac"', { "sources": [ "src/macos/PolicyWatcher.cc", + "src/macos/StringPolicy.cc", + "src/macos/NumberPolicy.cc" ], "defines": [ "MACOS", diff --git a/index.js b/index.js index 3359bab..69d2750 100644 --- a/index.js +++ b/index.js @@ -6,8 +6,11 @@ exports.createWatcher = require('bindings')('vscode-policy-watcher'); if (require.main === module) { + const platform = process.platform; exports.createWatcher( - 'CodeOSS', + platform === 'darwin' + ? 'com.visualstudio.code.oss' // MacOS + : 'CodeOSS', // Windows { UpdateMode: { type: 'string' }, SCMInputFontSize: { type: 'number' }, diff --git a/src/PolicyWatcher.hh b/src/PolicyWatcher.hh index 6aa4a7d..89b5d96 100644 --- a/src/PolicyWatcher.hh +++ b/src/PolicyWatcher.hh @@ -14,6 +14,11 @@ #include #endif +#ifdef MACOS +#define Boolean CFBoolean +#include +#endif + using namespace Napi; class PolicyWatcher : public AsyncProgressQueueWorker @@ -37,6 +42,13 @@ protected: #ifdef WINDOWS HANDLE handles[4]; #endif + +#ifdef MACOS + FSEventStreamRef stream; + dispatch_semaphore_t sem; + bool disposed; +#endif + }; #endif diff --git a/src/macos/NumberPolicy.cc b/src/macos/NumberPolicy.cc new file mode 100644 index 0000000..304db35 --- /dev/null +++ b/src/macos/NumberPolicy.cc @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#include "NumberPolicy.hh" + +using namespace Napi; + +NumberPolicy::NumberPolicy(const std::string name, const std::string &productName) + : PreferencesPolicy(name, productName) +{ +} + +Value NumberPolicy::getJSValue(Env env, long long value) const +{ + return Number::New(env, static_cast(value)); +} + +std::optional NumberPolicy::read() const +{ + auto pref = CFPreferencesCopyAppValue(key, appID); + + if (pref == NULL) + return std::nullopt; + + if (CFGetTypeID(pref) != CFNumberGetTypeID()) + { + CFRelease(pref); + return std::nullopt; + } + + long long value; + if (CFNumberGetValue((CFNumberRef)pref, kCFNumberLongLongType, &value)) + { + CFRelease(pref); + return value; + } + + CFRelease(pref); + return std::nullopt; +} diff --git a/src/macos/NumberPolicy.hh b/src/macos/NumberPolicy.hh new file mode 100644 index 0000000..633089f --- /dev/null +++ b/src/macos/NumberPolicy.hh @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#ifndef MAC_NUMBER_POLICY_H +#define MAC_NUMBER_POLICY_H + +#include +#include "PreferencesPolicy.hh" + +using namespace Napi; + +class NumberPolicy : public PreferencesPolicy +{ +public: + NumberPolicy(const std::string name, const std::string &productName); + std::optional read() const override; + +protected: + Value getJSValue(Env env, long long value) const override; +}; + +#endif diff --git a/src/macos/PolicyWatcher.cc b/src/macos/PolicyWatcher.cc index f7b8a89..198a682 100644 --- a/src/macos/PolicyWatcher.cc +++ b/src/macos/PolicyWatcher.cc @@ -4,22 +4,113 @@ *--------------------------------------------------------------------------------------------*/ #include "../PolicyWatcher.hh" +#include "StringPolicy.hh" +#include "NumberPolicy.hh" +#include using namespace Napi; +static void fsevents_callback(ConstFSEventStreamRef streamRef, + void *clientCallBackInfo, + size_t numEvents, + void *eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) +{ + dispatch_semaphore_t *sem = (dispatch_semaphore_t *)clientCallBackInfo; + dispatch_semaphore_signal(*sem); +} + PolicyWatcher::PolicyWatcher(std::string productName, const Function &okCallback) : AsyncProgressQueueWorker(okCallback), - productName(productName) + productName(productName), + stream(nullptr), + sem(nullptr), + disposed(false) { } PolicyWatcher::~PolicyWatcher() { + if (stream) { + FSEventStreamStop(stream); + FSEventStreamInvalidate(stream); + FSEventStreamRelease(stream); + } + if (sem) { + dispatch_release(sem); + } } -void PolicyWatcher::AddStringPolicy(const std::string name) {} -void PolicyWatcher::AddNumberPolicy(const std::string name) {} -void PolicyWatcher::OnExecute(Napi::Env env) {} -void PolicyWatcher::Execute(const ExecutionProgress &progress) {} -void PolicyWatcher::OnProgress(const Policy *const *policies, size_t count) {} -void PolicyWatcher::Dispose() {} +void PolicyWatcher::AddStringPolicy(const std::string name) +{ + policies.push_back(std::make_unique(name, productName)); +} +void PolicyWatcher::AddNumberPolicy(const std::string name) +{ + policies.push_back(std::make_unique(name, productName)); +} + +void PolicyWatcher::OnExecute(Napi::Env env) +{ + AsyncProgressQueueWorker::OnExecute(env); +} + +void PolicyWatcher::Execute(const ExecutionProgress &progress) +{ + std::vector updatedPolicies; + bool first = true; + + // Watch for changes + CFStringRef path = CFSTR("/Library/Managed Preferences/"); + sem = dispatch_semaphore_create(0); + FSEventStreamContext context = {0, &sem, NULL, NULL, NULL}; + stream = FSEventStreamCreate(NULL, + &fsevents_callback, + &context, + CFArrayCreate(NULL, (const void **)&path, 1, NULL), + kFSEventStreamEventIdSinceNow, + 1.0, + kFSEventStreamCreateFlagFileEvents); + + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + FSEventStreamSetDispatchQueue(stream, queue); + FSEventStreamStart(stream); + + while (!disposed) + { + updatedPolicies.clear(); + for (auto &policy : policies) + { + if (policy->refresh()) + { + updatedPolicies.push_back(policy.get()); + } + } + + if (first || updatedPolicies.size() > 0) + progress.Send(&updatedPolicies[0], updatedPolicies.size()); + + first = false; + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + } +} + +void PolicyWatcher::OnProgress(const Policy *const *policies, size_t count) +{ + HandleScope scope(Env()); + auto result = Object::New(Env()); + + for (size_t i = 0; i < count; i++) + result.Set(policies[i]->name, policies[i]->getValue(Env())); + + Callback().Call(Receiver().Value(), {result}); +} + +void PolicyWatcher::Dispose() +{ + disposed = true; + if (sem) { + dispatch_semaphore_signal(sem); + } +} diff --git a/src/macos/PreferencesPolicy.hh b/src/macos/PreferencesPolicy.hh new file mode 100644 index 0000000..5e44848 --- /dev/null +++ b/src/macos/PreferencesPolicy.hh @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#ifndef PREFERENCES_POLICY_H +#define PREFERENCES_POLICY_H + +#include +#include +#include "../Policy.hh" + +using namespace Napi; + +template +class PreferencesPolicy : public Policy +{ +public: + PreferencesPolicy(const std::string name, const std::string &productName) + : Policy(name), + appID(CFStringCreateWithCString(NULL, productName.c_str(), kCFStringEncodingUTF8)), + key(CFStringCreateWithCString(NULL, name.c_str(), kCFStringEncodingUTF8)) + { + } + + ~PreferencesPolicy() + { + CFRelease(appID); + CFRelease(key); + } + + bool refresh() + { + auto newValue = read(); + if (newValue.has_value()) + { + if (value != newValue) + { + value = newValue; + return true; + } + } + return false; + } + + Value getValue(Env env) const + { + if (!value.has_value()) + return env.Undefined(); + return getJSValue(env, *value); // value.value() is only supported after macOS 10.13 + } + +protected: + std::optional value; + const CFStringRef appID; + const CFStringRef key; + virtual Value getJSValue(Env env, T value) const = 0; + virtual std::optional read() const = 0; + +private: +}; +#endif \ No newline at end of file diff --git a/src/macos/StringPolicy.cc b/src/macos/StringPolicy.cc new file mode 100644 index 0000000..557f578 --- /dev/null +++ b/src/macos/StringPolicy.cc @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#include "StringPolicy.hh" + +using namespace Napi; + +StringPolicy::StringPolicy(const std::string name, const std::string &productName) + : PreferencesPolicy(name, productName) +{ +} + +Value StringPolicy::getJSValue(Env env, std::string value) const +{ + return String::New(env, value); +} + +std::optional StringPolicy::read() const +{ + auto pref = CFPreferencesCopyAppValue(key, appID); + + if (pref == NULL) + return std::nullopt; + + if (CFGetTypeID(pref) != CFStringGetTypeID()) + { + CFRelease(pref); + return std::nullopt; + } + + CFIndex length = CFStringGetLength((CFStringRef)pref); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + std::vector buffer(maxSize); + + if (CFStringGetCString((CFStringRef)pref, buffer.data(), maxSize, kCFStringEncodingUTF8)) + { + std::string result(buffer.data()); + CFRelease(pref); + return result; + } + + CFRelease(pref); + return std::nullopt; +} diff --git a/src/macos/StringPolicy.hh b/src/macos/StringPolicy.hh new file mode 100644 index 0000000..73730a6 --- /dev/null +++ b/src/macos/StringPolicy.hh @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#ifndef STRING_POLICY_H +#define STRING_POLICY_H + +#include +#include "PreferencesPolicy.hh" + +using namespace Napi; + +class StringPolicy : public PreferencesPolicy +{ +public: + StringPolicy(const std::string name, const std::string &productName); +protected: + Value getJSValue(Env env, std::string value) const override; + std::optional read() const override; +}; + +#endif \ No newline at end of file diff --git a/src/main.cc b/src/main.cc index 566e67d..95c5dae 100644 --- a/src/main.cc +++ b/src/main.cc @@ -22,7 +22,7 @@ Value CreateWatcher(const CallbackInfo &info) { auto env = info.Env(); -#ifndef WINDOWS +#if !defined(WINDOWS) && !defined(MACOS) throw TypeError::New(env, "Unsupported platform"); #endif @@ -55,18 +55,22 @@ Value CreateWatcher(const CallbackInfo &info) auto policyType = std::string(rawPolicyType.As()); - if (policyType == "string") + if (policyType == "string") { watcher->AddStringPolicy(rawPolicyName.As()); - else if (policyType == "number") + } + else if (policyType == "number") { watcher->AddNumberPolicy(rawPolicyName.As()); - else + } + else { throw TypeError::New(env, "Unknown policy type '" + policyType + "'"); + } } watcher->Queue(); auto result = Object::New(env); result.Set(String::New(env, "dispose"), Function::New(env, DisposeWatcher, "disposeWatcher", watcher)); + return result; } From 90dab7a41466e8ecfd44944390f342f87e3e1c06 Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:07:11 -0800 Subject: [PATCH 2/6] 1.2.0 (#48) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b021a4f..f2e4363 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vscode/policy-watcher", - "version": "1.1.10", + "version": "1.2.0", "description": "", "main": "index.js", "repository": { From 957a0f704f459fe152a7801749bfd695caf54419 Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Wed, 12 Mar 2025 09:29:10 -0700 Subject: [PATCH 3/6] React to removing configuration (#45) * change to more efficient kCFStreamEventNone * Mac: Respond to clearing a profile (https://github.com/microsoft/vscode/issues/241900) * mac: collapse conditions for readability * Windows: Respond to removing policy --- src/Policy.hh | 9 ++++++++- src/macos/PolicyWatcher.cc | 26 +++++++++++++++++++++----- src/macos/PreferencesPolicy.hh | 32 +++++++++++++++++++++----------- src/windows/PolicyWatcher.cc | 15 +++++++++++++-- src/windows/RegistryPolicy.hh | 20 +++++++++++++++----- 5 files changed, 78 insertions(+), 24 deletions(-) diff --git a/src/Policy.hh b/src/Policy.hh index 8e13551..62f9499 100644 --- a/src/Policy.hh +++ b/src/Policy.hh @@ -10,11 +10,18 @@ using namespace Napi; +enum class PolicyRefreshResult { + Updated, + Unchanged, + Removed, + NotSet +}; + class Policy { public: virtual ~Policy() {} - virtual bool refresh() = 0; + virtual PolicyRefreshResult refresh() = 0; virtual Value getValue(Env env) const = 0; const std::string name; diff --git a/src/macos/PolicyWatcher.cc b/src/macos/PolicyWatcher.cc index 198a682..499f38c 100644 --- a/src/macos/PolicyWatcher.cc +++ b/src/macos/PolicyWatcher.cc @@ -71,7 +71,7 @@ void PolicyWatcher::Execute(const ExecutionProgress &progress) CFArrayCreate(NULL, (const void **)&path, 1, NULL), kFSEventStreamEventIdSinceNow, 1.0, - kFSEventStreamCreateFlagFileEvents); + kCFStreamEventNone); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); FSEventStreamSetDispatchQueue(stream, queue); @@ -80,15 +80,25 @@ void PolicyWatcher::Execute(const ExecutionProgress &progress) while (!disposed) { updatedPolicies.clear(); + bool update = false; for (auto &policy : policies) - { - if (policy->refresh()) + { + switch(policy->refresh()) { - updatedPolicies.push_back(policy.get()); + case PolicyRefreshResult::Updated: + updatedPolicies.push_back(policy.get()); + update = true; + break; + case PolicyRefreshResult::Unchanged: + updatedPolicies.push_back(policy.get()); + break; + case PolicyRefreshResult::Removed: + update = true; + break; } } - if (first || updatedPolicies.size() > 0) + if (first || update) progress.Send(&updatedPolicies[0], updatedPolicies.size()); first = false; @@ -101,6 +111,12 @@ void PolicyWatcher::OnProgress(const Policy *const *policies, size_t count) HandleScope scope(Env()); auto result = Object::New(Env()); + if (count == 0) + { + Callback().Call(Receiver().Value(), {result}); + return; + } + for (size_t i = 0; i < count; i++) result.Set(policies[i]->name, policies[i]->getValue(Env())); diff --git a/src/macos/PreferencesPolicy.hh b/src/macos/PreferencesPolicy.hh index 5e44848..a9a578e 100644 --- a/src/macos/PreferencesPolicy.hh +++ b/src/macos/PreferencesPolicy.hh @@ -18,8 +18,8 @@ class PreferencesPolicy : public Policy public: PreferencesPolicy(const std::string name, const std::string &productName) : Policy(name), - appID(CFStringCreateWithCString(NULL, productName.c_str(), kCFStringEncodingUTF8)), - key(CFStringCreateWithCString(NULL, name.c_str(), kCFStringEncodingUTF8)) + appID(CFStringCreateWithCString(NULL, productName.c_str(), kCFStringEncodingUTF8)), + key(CFStringCreateWithCString(NULL, name.c_str(), kCFStringEncodingUTF8)) { } @@ -29,25 +29,35 @@ public: CFRelease(key); } - bool refresh() + PolicyRefreshResult refresh() { auto newValue = read(); - if (newValue.has_value()) + + // Check for no value or removal + if (!newValue.has_value()) { - if (value != newValue) - { - value = newValue; - return true; - } + if (!value.has_value()) + return PolicyRefreshResult::NotSet; + + value.reset(); + return PolicyRefreshResult::Removed; } - return false; + + // Is the value updated? + if (value != newValue) + { + value = newValue; + return PolicyRefreshResult::Updated; + } + + return PolicyRefreshResult::Unchanged; } Value getValue(Env env) const { if (!value.has_value()) return env.Undefined(); - return getJSValue(env, *value); // value.value() is only supported after macOS 10.13 + return getJSValue(env, *value); // value.value() is only supported after macOS 10.13 } protected: diff --git a/src/windows/PolicyWatcher.cc b/src/windows/PolicyWatcher.cc index b7107cd..9473c99 100644 --- a/src/windows/PolicyWatcher.cc +++ b/src/windows/PolicyWatcher.cc @@ -63,15 +63,26 @@ void PolicyWatcher::Execute(const ExecutionProgress &progress) { std::vector updatedPolicies; + bool update = false; + updatedPolicies.clear(); for (auto &policy : policies) { - if (policy->refresh()) + switch (policy->refresh()) { + case PolicyRefreshResult::Updated: updatedPolicies.push_back(policy.get()); + update = true; + break; + case PolicyRefreshResult::Unchanged: + updatedPolicies.push_back(policy.get()); + break; + case PolicyRefreshResult::Removed: + update = true; + break; } } - if (first || updatedPolicies.size() > 0) + if (first || update) progress.Send(&updatedPolicies[0], updatedPolicies.size()); first = false; diff --git a/src/windows/RegistryPolicy.hh b/src/windows/RegistryPolicy.hh index c0cf582..1ebd94d 100644 --- a/src/windows/RegistryPolicy.hh +++ b/src/windows/RegistryPolicy.hh @@ -24,7 +24,7 @@ public: registryKey("Software\\Policies\\Microsoft\\" + productName), supportedTypes(types) {} - bool refresh() + PolicyRefreshResult refresh() { auto machine = read(HKEY_LOCAL_MACHINE); @@ -33,21 +33,31 @@ public: if (value != machine) { value = machine; - return true; + return PolicyRefreshResult::Updated; } - return false; + return PolicyRefreshResult::Unchanged; } auto user = read(HKEY_CURRENT_USER); + // Check for no value or removal + if (!user.has_value()) + { + if (!value.has_value()) + return PolicyRefreshResult::NotSet; + + value.reset(); + return PolicyRefreshResult::Removed; + } + if (value != user) { value = user; - return true; + return PolicyRefreshResult::Updated; } - return false; + return PolicyRefreshResult::Unchanged; } Value getValue(Env env) const From cdd95689a3a1dfdd933111f1aaefaf459518a431 Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:17:37 -0700 Subject: [PATCH 4/6] 1.2.1 (#49) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2e4363..6c5569a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vscode/policy-watcher", - "version": "1.2.0", + "version": "1.2.1", "description": "", "main": "index.js", "repository": { From 71510345d57dd5ffb0dfbac5c64f2cd565de99ce Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:34:36 -0700 Subject: [PATCH 5/6] Add BooleanPolicy (#50) * Add BooleanPolicy and implement for macOS * windows --- binding.gyp | 6 ++++-- index.d.ts | 7 +++++-- index.js | 1 + src/PolicyWatcher.hh | 1 + src/linux/PolicyWatcher.cc | 1 + src/macos/BooleanPolicy.cc | 36 ++++++++++++++++++++++++++++++++++++ src/macos/BooleanPolicy.hh | 24 ++++++++++++++++++++++++ src/macos/PolicyWatcher.cc | 6 ++++++ src/main.cc | 5 +++-- src/windows/BooleanPolicy.cc | 28 ++++++++++++++++++++++++++++ src/windows/BooleanPolicy.hh | 25 +++++++++++++++++++++++++ src/windows/PolicyWatcher.cc | 6 ++++++ 12 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 src/macos/BooleanPolicy.cc create mode 100644 src/macos/BooleanPolicy.hh create mode 100644 src/windows/BooleanPolicy.cc create mode 100644 src/windows/BooleanPolicy.hh diff --git a/binding.gyp b/binding.gyp index e31f795..bec47f5 100644 --- a/binding.gyp +++ b/binding.gyp @@ -17,7 +17,8 @@ "sources": [ "src/macos/PolicyWatcher.cc", "src/macos/StringPolicy.cc", - "src/macos/NumberPolicy.cc" + "src/macos/NumberPolicy.cc", + "src/macos/BooleanPolicy.cc" ], "defines": [ "MACOS", @@ -40,7 +41,8 @@ "sources": [ "src/windows/PolicyWatcher.cc", "src/windows/StringPolicy.cc", - "src/windows/NumberPolicy.cc" + "src/windows/NumberPolicy.cc", + "src/windows/BooleanPolicy.cc" ], "defines": [ "WINDOWS" diff --git a/index.d.ts b/index.d.ts index 2429260..d4663f4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -9,9 +9,10 @@ interface Watcher { type StringPolicy = { type: "string" }; type NumberPolicy = { type: "number" }; +type BooleanPolicy = { type: "boolean" }; export interface Policies { - [policyName: string]: StringPolicy | NumberPolicy; + [policyName: string]: StringPolicy | NumberPolicy | BooleanPolicy; } export type PolicyUpdate = { @@ -19,9 +20,11 @@ export type PolicyUpdate = { | undefined | (T[K] extends StringPolicy ? string + : (T[K] extends BooleanPolicy + ? boolean : T[K] extends NumberPolicy ? number - : never); + : never)); }; export function createWatcher( diff --git a/index.js b/index.js index 69d2750..8a8cc01 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ if (require.main === module) { { UpdateMode: { type: 'string' }, SCMInputFontSize: { type: 'number' }, + DisableFeedback: { type: 'boolean' }, }, msg => console.log(msg) ); diff --git a/src/PolicyWatcher.hh b/src/PolicyWatcher.hh index 89b5d96..0441bf1 100644 --- a/src/PolicyWatcher.hh +++ b/src/PolicyWatcher.hh @@ -29,6 +29,7 @@ public: void AddStringPolicy(const std::string name); void AddNumberPolicy(const std::string name); + void AddBooleanPolicy(const std::string name); void OnExecute(Napi::Env env); void Execute(const ExecutionProgress &progress); diff --git a/src/linux/PolicyWatcher.cc b/src/linux/PolicyWatcher.cc index f7b8a89..cb8b6d8 100644 --- a/src/linux/PolicyWatcher.cc +++ b/src/linux/PolicyWatcher.cc @@ -19,6 +19,7 @@ PolicyWatcher::~PolicyWatcher() void PolicyWatcher::AddStringPolicy(const std::string name) {} void PolicyWatcher::AddNumberPolicy(const std::string name) {} +void PolicyWatcher::AddBooleanPolicy(const std::string name) {} void PolicyWatcher::OnExecute(Napi::Env env) {} void PolicyWatcher::Execute(const ExecutionProgress &progress) {} void PolicyWatcher::OnProgress(const Policy *const *policies, size_t count) {} diff --git a/src/macos/BooleanPolicy.cc b/src/macos/BooleanPolicy.cc new file mode 100644 index 0000000..072a923 --- /dev/null +++ b/src/macos/BooleanPolicy.cc @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#include "BooleanPolicy.hh" + +using namespace Napi; + +BooleanPolicy::BooleanPolicy(const std::string name, const std::string &productName) + : PreferencesPolicy(name, productName) +{ +} + +Value BooleanPolicy::getJSValue(Env env, bool value) const +{ + return Napi::Boolean::New(env, value); +} + +std::optional BooleanPolicy::read() const +{ + auto pref = CFPreferencesCopyAppValue(key, appID); + + if (pref == NULL) + return std::nullopt; + + if (CFGetTypeID(pref) != CFBooleanGetTypeID()) + { + CFRelease(pref); + return std::nullopt; + } + + bool value = (pref == kCFBooleanTrue); + CFRelease(pref); + return value; +} diff --git a/src/macos/BooleanPolicy.hh b/src/macos/BooleanPolicy.hh new file mode 100644 index 0000000..9abc6fb --- /dev/null +++ b/src/macos/BooleanPolicy.hh @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#ifndef MAC_BOOLEAN_POLICY_H +#define MAC_BOOLEAN_POLICY_H + +#include +#include "PreferencesPolicy.hh" + +using namespace Napi; + +class BooleanPolicy : public PreferencesPolicy +{ +public: + BooleanPolicy(const std::string name, const std::string &productName); + std::optional read() const override; + +protected: + Value getJSValue(Env env, bool value) const override; +}; + +#endif diff --git a/src/macos/PolicyWatcher.cc b/src/macos/PolicyWatcher.cc index 499f38c..14bc869 100644 --- a/src/macos/PolicyWatcher.cc +++ b/src/macos/PolicyWatcher.cc @@ -6,6 +6,7 @@ #include "../PolicyWatcher.hh" #include "StringPolicy.hh" #include "NumberPolicy.hh" +#include "BooleanPolicy.hh" #include using namespace Napi; @@ -51,6 +52,11 @@ void PolicyWatcher::AddNumberPolicy(const std::string name) policies.push_back(std::make_unique(name, productName)); } +void PolicyWatcher::AddBooleanPolicy(const std::string name) +{ + policies.push_back(std::make_unique(name, productName)); +} + void PolicyWatcher::OnExecute(Napi::Env env) { AsyncProgressQueueWorker::OnExecute(env); diff --git a/src/main.cc b/src/main.cc index 95c5dae..53ae082 100644 --- a/src/main.cc +++ b/src/main.cc @@ -60,8 +60,9 @@ Value CreateWatcher(const CallbackInfo &info) } else if (policyType == "number") { watcher->AddNumberPolicy(rawPolicyName.As()); - } - else { + } else if (policyType == "boolean") { + watcher->AddBooleanPolicy(rawPolicyName.As()); + } else { throw TypeError::New(env, "Unknown policy type '" + policyType + "'"); } } diff --git a/src/windows/BooleanPolicy.cc b/src/windows/BooleanPolicy.cc new file mode 100644 index 0000000..dc28728 --- /dev/null +++ b/src/windows/BooleanPolicy.cc @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#include "BooleanPolicy.hh" +#include + +using namespace Napi; + +BooleanPolicy::BooleanPolicy(const std::string& name, const std::string& productName) + : RegistryPolicy(name, productName, {REG_DWORD}) {} + +bool BooleanPolicy::parseRegistryValue(LPBYTE buffer, DWORD bufferSize, DWORD type) const +{ + if (type != REG_DWORD || bufferSize != sizeof(DWORD)) + { + return false; + } + + DWORD value = *reinterpret_cast(buffer); + return (value != 0); +} + +Value BooleanPolicy::getJSValue(Env env, bool value) const +{ + return Boolean::New(env, value); +} diff --git a/src/windows/BooleanPolicy.hh b/src/windows/BooleanPolicy.hh new file mode 100644 index 0000000..00d6cb0 --- /dev/null +++ b/src/windows/BooleanPolicy.hh @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +#ifndef BOOLEAN_POLICY_H +#define BOOLEAN_POLICY_H + +#include +#include +#include "RegistryPolicy.hh" + +using namespace Napi; + +class BooleanPolicy : public RegistryPolicy +{ +public: + BooleanPolicy(const std::string& name, const std::string &productName); + +protected: + bool parseRegistryValue(LPBYTE buffer, DWORD bufferSize, DWORD type) const; + Value getJSValue(Env env, bool value) const; +}; + +#endif \ No newline at end of file diff --git a/src/windows/PolicyWatcher.cc b/src/windows/PolicyWatcher.cc index 9473c99..6a063ee 100644 --- a/src/windows/PolicyWatcher.cc +++ b/src/windows/PolicyWatcher.cc @@ -8,6 +8,7 @@ #include "../PolicyWatcher.hh" #include "StringPolicy.hh" #include "NumberPolicy.hh" +#include "BooleanPolicy.hh" using namespace Napi; @@ -37,6 +38,11 @@ void PolicyWatcher::AddNumberPolicy(const std::string name) policies.push_back(std::make_unique(name, productName)); } +void PolicyWatcher::AddBooleanPolicy(const std::string name) +{ + policies.push_back(std::make_unique(name, productName)); +} + void PolicyWatcher::OnExecute(Napi::Env env) { if ((handles[0] = CreateEvent(NULL, false, false, NULL)) == NULL) From b0cccedad2725c75473ef9af861ea6f83feece7f Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Fri, 14 Mar 2025 11:09:41 -0700 Subject: [PATCH 6/6] 1.3.0 (#51) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c5569a..d8862fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@vscode/policy-watcher", - "version": "1.2.1", + "version": "1.3.0", "description": "", "main": "index.js", "repository": {