UNPKG

gpii-windows

Version:

Components of the GPII personalization infrastructure for use on Microsoft's "Windows" ™

503 lines (424 loc) 17 kB
/* * Windows Native Settings Handler * * Deals with individual settings that are only accessible throught isolated * API calls. * * Copyright 2018 Raising the Floor - International * * Licensed under the New BSD license. You may not use this file except in * compliance with this License. * * The research leading to these results has received funding from the European Union's * Seventh Framework Programme (FP7/2007-2013) * under grant agreement no. 289016. * * You may obtain a copy of the License at * https://github.com/GPII/universal/blob/master/LICENSE.txt */ "use strict"; var fluid = require("gpii-universal"), gpii = fluid.registerNamespace("gpii"), windows = fluid.registerNamespace("gpii.windows"), ref = require("ref"), child_process = require("child_process"), path = require("path"); require("../../WindowsUtilities/WindowsUtilities.js"); fluid.registerNamespace("gpii.windows.nativeSettingsHandler"); /** * Gets the current double click time for mouse. * * @return {Promise} A promise holding the current system double click time for mouse. */ windows.nativeSettingsHandler.GetDoubleClickTime = function () { var result = windows.user32.GetDoubleClickTime(); return fluid.toPromise(result); }; /** * Mirrors primary screen into a secondary screen. * * Note: * The native function 'SetDisplayConfig' returns the error 'ERROR_GEN_FAILURE' which stands to code * 31 when a second display isn't present, this error may be misleading because no system component * is malfunctioning. * * @param {Boolean} mirror A boolean representing the desired state for the 'screen mirror' feature. * @return {Promise} A promise that resolves on success or holds a error code and message on failure. */ windows.nativeSettingsHandler.SetScreenMirrored = function (mirror) { var pRes = fluid.promise(); var err = 0; var flags = mirror ? (windows.API_constants.SDC_TOPOLOGY_CLONE | windows.API_constants.SDC_APPLY) : (windows.API_constants.SDC_TOPOLOGY_INTERNAL | windows.API_constants.SDC_APPLY); err = gpii.windows.user32.SetDisplayConfig(0, ref.NULL, 0, ref.NULL, flags); if (err === 0) { pRes.resolve(); } else { pRes.reject({code: err, msg: windows.translateLastError(err)}); } return pRes; }; /** * Queries if the main display is currently mirrored. * * @return {Promise} A promise holding a bool with the result. */ windows.nativeSettingsHandler.GetScreenMirrored = function () { // The size of the structs for holding the display config information. var DISPLAYCONFIG_PATH_INFO_SIZE = 72; var DISPLAYCONFIG_MODE_INFO_SIZE = 64; var pModePathArrayElem = ref.alloc(ref.types.uint32, 1); var pModeInfoArrayElem = ref.alloc(ref.types.uint32, 1); gpii.windows.user32.GetDisplayConfigBufferSizes(4, pModePathArrayElem, pModeInfoArrayElem); var topologyIdBuf = ref.alloc(ref.types.uint32, 0); var pModePathArray = Buffer.alloc(DISPLAYCONFIG_PATH_INFO_SIZE * pModePathArrayElem.deref()); var pModeInfoArray = Buffer.alloc(DISPLAYCONFIG_MODE_INFO_SIZE * pModeInfoArrayElem.deref()); var pRes = fluid.promise(); var err = gpii.windows.user32.QueryDisplayConfig( 4, pModePathArrayElem, pModePathArray, pModeInfoArrayElem, pModeInfoArray, topologyIdBuf ); if (err === 0) { var screenCloned = topologyIdBuf.deref() === windows.API_constants.SDC_TOPOLOGY_CLONE; pRes.resolve(screenCloned); } else { pRes.reject({code: err, msg: windows.translateLastError(err)}); } return pRes; }; /** * Sets the current double click time for mouse. * * @param {Number} num The double click time to set in the system. * @return {Promise} A promise that resolves on success or holds a object with the error information * when rejected. */ windows.nativeSettingsHandler.SetDoubleClickTime = function (num) { var pRes = fluid.promise(); var retVal = windows.user32.SetDoubleClickTime(num); if (retVal !== 0) { pRes.resolve(); } else { var lastErr = windows.kernel32.GetLastError(); var lastErrMsg = windows.translateLastError(lastErr); pRes.reject({code: lastErr, msg: lastErrMsg}); } return pRes; }; /** * Set Windows background solid color. * * @param {Object} rgbColor The color in RGB format, as and object of type: { r: 0x00, g: 0x00, b: 0x00 } * @return {Promise} Resolve or rejects if failing to set the new system color. */ windows.nativeSettingsHandler.SetSolidColor = function (rgbColor) { var pRes = fluid.promise(); var color = rgbColor.r | rgbColor.g << 8 | rgbColor.b << 16; var bufElems = ref.alloc(windows.types.INT, windows.API_constants.COLOR_DESKTOP); var bufColors = ref.alloc(windows.types.UINT, color); var res = windows.user32.SetSysColors(1, bufElems, bufColors); if (res === 0) { pRes.reject("nativeSettingsHandler: Failed to set new system color."); } else { pRes.resolve(); } return pRes; }; /** * Returns the current Windows dekstop solid color as a RGB color object of format: { r: 0x00, g: 0x00, b: 0x00 } * * @return {Promise} Resolves containing the current system solid color value. */ windows.nativeSettingsHandler.GetSolidColor = function () { var pRes = fluid.promise(); var decimal = windows.user32.GetSysColor(windows.API_constants.COLOR_DESKTOP); pRes.resolve({ r: (decimal & 0x0000ff), g: (decimal & 0x00ff00) >> 8, b: (decimal & 0xff0000) >> 16 }); return pRes; }; /** * Sets the width of the double-click rectangle. * * @param {Number} num The number to be set as the width of the double-click rectangle. * @return {Promise} A promise that resolves on success or holds a object with the error information * when rejected. */ windows.nativeSettingsHandler.SetDoubleClickWidth = function (num) { var pRes = fluid.promise(); var retVal = gpii.windows.spi.systemParametersInfo("UINT", gpii.windows.spi.actions.SPI_SETDOUBLECLKWIDTH, num, 0, 0); if (retVal === 0) { var lastErr = windows.kernel32.GetLastError(); var lastErrMsg = windows.translateLastError(lastErr); pRes.reject({code: lastErr, msg: lastErrMsg}); } else { pRes.resolve(retVal); } return pRes; }; /** * Gets the current width of the double-click rectangle. * * @return {Promise} A promise holding the current value of the width for the double-click rectangle. */ windows.nativeSettingsHandler.GetDoubleClickWidth = function () { var result = windows.user32.GetSystemMetrics(windows.API_constants.SM_CXDOUBLECLK); return fluid.toPromise(result); }; /** * Sets the height of the double-click rectangle. * * @param {Number} num The number to be set as the height of the double-click rectangle. * @return {Promise} A promise that resolves on success or holds a object with the error information * when rejected. */ windows.nativeSettingsHandler.SetDoubleClickHeight = function (num) { var pRes = fluid.promise(); var retVal = gpii.windows.spi.systemParametersInfo("UINT", gpii.windows.spi.actions.SPI_SETDOUBLECLKHEIGHT, num, 0, 0); if (retVal === 0) { var lastErr = windows.kernel32.GetLastError(); var lastErrMsg = windows.translateLastError(lastErr); pRes.reject({code: lastErr, msg: lastErrMsg}); } else { pRes.resolve(retVal); } return pRes; }; /** * Function that handles calling the native executable for volume control. * * @param {String} mode The operation mode, could be either "Set" or "Get". * @param {Number} [num] The value to set as the current system volume. Range is normalized from 0 to 1. * @return {Promise} A promise that resolves on success or holds a object with the error information. */ windows.nativeSettingsHandler.VolumeHandler = function (mode, num) { var promise = fluid.promise(); try { var fileName = path.join(__dirname, "../nativeSolutions/VolumeControl/Release/VolumeControl.exe"); var valueBuff; if (mode === "Get") { valueBuff = child_process.execFileSync(fileName, ["Get"], {}); } else { valueBuff = child_process.execFileSync(fileName, ["Set", num], {}); } var strRes = valueBuff.toString("utf8"); var jsonRes = JSON.parse(strRes); var value = fluid.get(jsonRes, "Value"); if (value) { promise.resolve(parseFloat(value)); } else { promise.reject(jsonRes); } } catch (err) { promise.reject(err); } return promise; }; /** * Gets the current system volume, calling the native executable 'VolumeControl.exe' * with the proper payload. * * @return {Promise} A promise that resolves on success or holds a object with the error information. */ windows.nativeSettingsHandler.GetVolume = function () { return windows.nativeSettingsHandler.VolumeHandler("Get"); }; /** * Sets the current system volume, calling the native executable 'VolumeControl.exe' * with the proper payload. * * @param {Number} num The value to set as the current system volume. Range is normalized from 0 to 1. * @return {Promise} A promise that resolves on success or holds a object with the error information. */ windows.nativeSettingsHandler.SetVolume = function (num) { return windows.nativeSettingsHandler.VolumeHandler("Set", num); }; /** * Gets the current height of the double-click rectangle. * * @return {Promise} A promise holding the current value of the height for the double-click rectangle. */ windows.nativeSettingsHandler.GetDoubleClickHeight = function () { var result = windows.user32.GetSystemMetrics(windows.API_constants.SM_CYDOUBLECLK); return fluid.toPromise(result); }; windows.nativeSettingsHandler.functions = { SolidColor: { set: windows.nativeSettingsHandler.SetSolidColor, get: windows.nativeSettingsHandler.GetSolidColor }, DoubleClickTime: { set: windows.nativeSettingsHandler.SetDoubleClickTime, get: windows.nativeSettingsHandler.GetDoubleClickTime }, DoubleClickWidth: { set: windows.nativeSettingsHandler.SetDoubleClickWidth, get: windows.nativeSettingsHandler.GetDoubleClickWidth }, DoubleClickHeight: { set: windows.nativeSettingsHandler.SetDoubleClickHeight, get: windows.nativeSettingsHandler.GetDoubleClickHeight }, ScreenMirror: { set: windows.nativeSettingsHandler.SetScreenMirrored, get: windows.nativeSettingsHandler.GetScreenMirrored }, Volume: { set: windows.nativeSettingsHandler.SetVolume, get: windows.nativeSettingsHandler.GetVolume } }; /** * Helper function that converts a error to string, so the rejection from the * settings handler only holds a message. * * @param {Object} err The error to be stringified. * @return {String} The error message. */ windows.nativeSettingsHandler.errorInfo = function (err) { return "With error - '" + JSON.stringify(err) + "'"; }; /** * Helper function for creating a custom message for error rejection. * * @param {Promise} promise The promise to be rejected. * @param {String} settingKey The name of the requested setting for which an operation failed. * @param {Object} err The error object. * @param {String} message A message with information about the operation. */ windows.nativeSettingsHandler.reject = function (promise, settingKey, err, message) { var operation = message + settingKey + "' failed. "; var errMsg = "nativeSettingsHandler" + operation + windows.nativeSettingsHandler.errorInfo(err); promise.reject(errMsg); }; /** * Setter for the nativeSettingsHandler. * * The payload should have the following format: * * { * "functionName": "FunctionName", * "setParam": val * } * * The first two items: * - function: Function name for the function to be used to set/get the value. * - setParam: Parameter to be passed to the 'set' function. * * @param {Object} payload The payload. * @return {Promise} Resolves with the response. */ windows.nativeSettingsHandler.setImpl = function (payload) { var pRes = fluid.promise(); var fnName = fluid.get(payload, "options.functionName"); var setFn = fluid.get(windows.nativeSettingsHandler.functions, fnName + ".set"); var getFn = fluid.get(windows.nativeSettingsHandler.functions, fnName + ".get"); var fnNotDef = [": Failed due to '", " " + fnName + "' function not being defined."]; if (setFn === undefined) { pRes.reject("nativeSettingsHandler" + fnNotDef[0] + "set" + fnNotDef[1]); } else if (getFn === undefined) { pRes.reject("nativeSettingsHandler" + fnNotDef[0] + "get" + fnNotDef[1]); } else { var results = {}; var settingsArray = fluid.makeArray(payload.settings); if (settingsArray.length === 0) { pRes.reject("nativeSettingsHandler: Failed due to empty payload."); } else { var uniqueSetting = settingsArray[0]; // Get the payload value to set. var settingKey = fluid.keys(uniqueSetting)[0]; var valueToSet = fluid.values(uniqueSetting)[0].value; // Create object for storing setting results. results[settingKey] = {}; // Get the current values for the setting. var pGetOld = getFn(); pGetOld.then( function (oldVal) { // Store the current value in results. fluid.set(results[settingKey], "oldValue.value", oldVal); var pSet = setFn(valueToSet); pSet.then( function () { var pGetNew = getFn(); pGetNew.then( function (newVal) { fluid.set(results[settingKey], "newValue.value", newVal); pRes.resolve(results); }, function (err) { windows.nativeSettingsHandler.reject(pRes, settingKey, err, ": Getting new value for setting '"); } ); }, function (err) { windows.nativeSettingsHandler.reject(pRes, settingKey, err, ": Setting new value for setting '"); } ); }, function (err) { windows.nativeSettingsHandler.reject(pRes, settingKey, err, ": Getting current value for setting '"); } ); } } return pRes; }; /** * Getter for the nativeSettingsHandler. * * @param {Object} payload The payload. * @return {Promise} Resolves with the response. */ windows.nativeSettingsHandler.getImpl = function (payload) { var pRes = fluid.promise(); var fnName = fluid.get(payload, "options.functionName"); var getFn = fluid.get(windows.nativeSettingsHandler.functions, fnName + ".get"); var fnNotDef = [": Failed due to '", " " + fnName + "' function not being defined."]; if (getFn === undefined) { pRes.reject("nativeSettingsHandler" + fnNotDef[0] + "get" + fnNotDef[1]); } else { var settingsArray = fluid.makeArray(payload.settings); if (settingsArray.length === 0) { pRes.reject("nativeSettingsHandler: Failed due to empty payload."); } else { var uniqueSetting = settingsArray[0]; var results = {}; // Get payload setting and prepare results for returning it var settingKey = fluid.keys(uniqueSetting)[0]; results[settingKey] = {}; var pGetRes = getFn(); pGetRes.then( function (curVal) { results[settingKey].value = curVal; pRes.resolve(results); }, function (err) { windows.nativeSettingsHandler.reject(pRes, settingKey, err, ": Getting current value for setting '"); } ); } } return pRes; }; /** * Invoke the settings handler. * * @param {Object} payload The payload * @return {Promise} Resolves with the response. */ windows.nativeSettingsHandler.get = function (payload) { return gpii.settingsHandlers.invokeSettingsHandler(windows.nativeSettingsHandler.getImpl, payload); }; /** * Invoke the settings handler. * * @param {Object} payload The payload * @return {Promise} Resolves with the response. */ windows.nativeSettingsHandler.set = function (payload) { return gpii.settingsHandlers.invokeSettingsHandler(windows.nativeSettingsHandler.setImpl, payload); };