1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "native_screenshot_module.h"
16 
17 #include <cinttypes>
18 #include <cstddef>
19 #include <cstdint>
20 #include <image_type.h>
21 #include <iosfwd>
22 #include <js_native_api.h>
23 #include <js_native_api_types.h>
24 #include <memory>
25 #include <napi/native_api.h>
26 #include <napi/native_common.h>
27 #include <string>
28 #include <type_traits>
29 
30 #include "display_manager.h"
31 #include "pixel_map.h"
32 #include "pixel_map_napi.h"
33 #include "window_manager_hilog.h"
34 #include "dm_common.h"
35 #include "dm_napi_common.h"
36 
37 namespace OHOS::Rosen {
38 namespace save {
39 struct Option {
40     Media::Rect rect;
41     Media::Size size;
42     int rotation = 0;
43     DisplayId displayId = 0;
44 };
45 
46 struct Param {
47     DmErrorCode wret;
48     Option option;
49     std::string errMessage;
50     bool useInputOption;
51     bool validInputParam;
52     std::shared_ptr<Media::PixelMap> image;
53     Media::Rect imageRect;
54     bool isPick;
55 };
56 
GetType(napi_env env, napi_value root)57 static napi_valuetype GetType(napi_env env, napi_value root)
58 {
59     napi_valuetype res = napi_undefined;
60     napi_typeof(env, root, &res);
61     return res;
62 }
63 
GetDisplayId(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)64 static void GetDisplayId(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)
65 {
66     GNAPI_LOG("Get Screenshot Option: GetDisplayId");
67     napi_value displayId;
68     NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, argv, "displayId", &displayId));
69     if (displayId != nullptr && GetType(env, displayId) == napi_number) {
70         int64_t dispId;
71         NAPI_CALL_RETURN_VOID(env, napi_get_value_int64(env, displayId, &dispId));
72         param->option.displayId = static_cast<DisplayId>(dispId);
73         GNAPI_LOG("GetDisplayId success, displayId = %{public}" PRIu64"", param->option.displayId);
74     } else {
75         GNAPI_LOG("GetDisplayId failed, invalid param, use default displayId = 0");
76     }
77 }
78 
GetRotation(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)79 static void GetRotation(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)
80 {
81     GNAPI_LOG("Get Screenshot Option: GetRotation");
82     napi_value rotation;
83     NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, argv, "rotation", &rotation));
84     if (rotation != nullptr && GetType(env, rotation) == napi_number) {
85         NAPI_CALL_RETURN_VOID(env, napi_get_value_int32(env, rotation, &param->option.rotation));
86         GNAPI_LOG("GetRotation success, rotation = %{public}d", param->option.rotation);
87     } else {
88         GNAPI_LOG("GetRotation failed, invalid param, use default rotation = 0");
89     }
90 }
91 
GetScreenRect(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)92 static void GetScreenRect(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)
93 {
94     GNAPI_LOG("Get Screenshot Option: GetScreenRect");
95     napi_value screenRect;
96     NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, argv, "screenRect", &screenRect));
97     if (screenRect != nullptr && GetType(env, screenRect) == napi_object) {
98         GNAPI_LOG("get ScreenRect success");
99 
100         napi_value left;
101         NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, screenRect, "left", &left));
102         if (left != nullptr && GetType(env, left) == napi_number) {
103             NAPI_CALL_RETURN_VOID(env, napi_get_value_int32(env, left, &param->option.rect.left));
104             GNAPI_LOG("get ScreenRect.left success, left = %{public}d", param->option.rect.left);
105         } else {
106             GNAPI_LOG("get ScreenRect.left failed, invalid param, use default left = 0");
107         }
108 
109         napi_value top;
110         NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, screenRect, "top", &top));
111         if (top != nullptr && GetType(env, top) == napi_number) {
112             NAPI_CALL_RETURN_VOID(env, napi_get_value_int32(env, top, &param->option.rect.top));
113             GNAPI_LOG("get ScreenRect.top success, top = %{public}d", param->option.rect.top);
114         } else {
115             GNAPI_LOG("get ScreenRect.top failed, invalid param, use default top = 0");
116         }
117 
118         napi_value width;
119         NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, screenRect, "width", &width));
120         if (width != nullptr && GetType(env, width) == napi_number) {
121             NAPI_CALL_RETURN_VOID(env, napi_get_value_int32(env, width, &param->option.rect.width));
122             GNAPI_LOG("get ScreenRect.width success, width = %{public}d", param->option.rect.width);
123         } else {
124             GNAPI_LOG("get ScreenRect.width failed, invalid param, use default width = 0");
125         }
126 
127         napi_value height;
128         NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, screenRect, "height", &height));
129         if (height != nullptr && GetType(env, height) == napi_number) {
130             NAPI_CALL_RETURN_VOID(env, napi_get_value_int32(env, height, &param->option.rect.height));
131             GNAPI_LOG("get ScreenRect.height success, height = %{public}d", param->option.rect.height);
132         } else {
133             GNAPI_LOG("get ScreenRect.height failed, invalid param, use default height = 0");
134         }
135     } else {
136         GNAPI_LOG("get ScreenRect failed, use default ScreenRect param");
137     }
138 }
139 
GetImageSize(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)140 static void GetImageSize(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)
141 {
142     GNAPI_LOG("Get Screenshot Option: ImageSize");
143     napi_value imageSize;
144     NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, argv, "imageSize", &imageSize));
145     if (imageSize != nullptr && GetType(env, imageSize) == napi_object) {
146         napi_value width;
147         NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, imageSize, "width", &width));
148         if (width != nullptr && GetType(env, width) == napi_number) {
149             NAPI_CALL_RETURN_VOID(env, napi_get_value_int32(env, width, &param->option.size.width));
150             GNAPI_LOG("get ImageSize.width success, width = %{public}d", param->option.size.width);
151         } else {
152             GNAPI_LOG("get ImageSize.width failed, invalid param, use default width = 0");
153         }
154 
155         napi_value height;
156         NAPI_CALL_RETURN_VOID(env, napi_get_named_property(env, imageSize, "height", &height));
157         if (height != nullptr && GetType(env, height) == napi_number) {
158             NAPI_CALL_RETURN_VOID(env, napi_get_value_int32(env, height, &param->option.size.height));
159             GNAPI_LOG("get ImageSize.height success, height = %{public}d", param->option.size.height);
160         } else {
161             GNAPI_LOG("get ImageSize.height failed, invalid param, use default height = 0");
162         }
163     }
164 }
165 
GetScreenshotParam(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)166 static void GetScreenshotParam(napi_env env, std::unique_ptr<Param> &param, napi_value &argv)
167 {
168     if (param == nullptr) {
169         GNAPI_LOG("param == nullptr, use default param");
170         return;
171     }
172     GetDisplayId(env, param, argv);
173     GetRotation(env, param, argv);
174     GetScreenRect(env, param, argv);
175     GetImageSize(env, param, argv);
176 }
177 
AsyncGetScreenshot(napi_env env, std::unique_ptr<Param> &param)178 static void AsyncGetScreenshot(napi_env env, std::unique_ptr<Param> &param)
179 {
180     if (!param->validInputParam) {
181         WLOGFE("Invalid Input Param!");
182         param->image = nullptr;
183         param->wret = DmErrorCode::DM_ERROR_INVALID_PARAM;
184         param->errMessage = "Get Screenshot Failed: Invalid input param";
185         return;
186     }
187     if (param->useInputOption) {
188         GNAPI_LOG("Get Screenshot by input option");
189         param->image = DisplayManager::GetInstance().GetScreenshot(param->option.displayId,
190             param->option.rect, param->option.size, param->option.rotation, &param->wret);
191     } else if (param->isPick) {
192         GNAPI_LOG("Get Screenshot by picker");
193         param->image = DisplayManager::GetInstance().GetSnapshotByPicker(param->imageRect, &param->wret);
194     } else {
195         GNAPI_LOG("Get Screenshot by default option");
196         param->image = DisplayManager::GetInstance().GetScreenshot(param->option.displayId, &param->wret);
197     }
198     if (param->image == nullptr && param->wret == DmErrorCode::DM_OK) {
199         GNAPI_LOG("Get Screenshot failed!");
200         param->wret = DmErrorCode::DM_ERROR_INVALID_SCREEN;
201         param->errMessage = "Get Screenshot failed: Screenshot image is nullptr";
202         return;
203     }
204 }
205 
CreateJsNumber(napi_env env, int32_t value)206 napi_value CreateJsNumber(napi_env env, int32_t value)
207 {
208     napi_value valRet = nullptr;
209     napi_create_int32(env, value, &valRet);
210     return valRet;
211 }
212 
CreateJsRectObject(napi_env env, Media::Rect imageRect)213 napi_value CreateJsRectObject(napi_env env, Media::Rect imageRect)
214 {
215     napi_value objValue = nullptr;
216     NAPI_CALL(env, napi_create_object(env, &objValue));
217     napi_set_named_property(env, objValue, "left", CreateJsNumber(env, imageRect.left));
218     napi_set_named_property(env, objValue, "top", CreateJsNumber(env, imageRect.top));
219     napi_set_named_property(env, objValue, "width", CreateJsNumber(env, imageRect.width));
220     napi_set_named_property(env, objValue, "height", CreateJsNumber(env, imageRect.height));
221     return objValue;
222 }
223 
CreateJsPickerObject(napi_env env, std::unique_ptr<Param> &param)224 napi_value CreateJsPickerObject(napi_env env, std::unique_ptr<Param> &param)
225 {
226     napi_value objValue = nullptr;
227     NAPI_CALL(env, napi_create_object(env, &objValue));
228     if (param == nullptr) {
229         napi_value result;
230         WLOGFE("param nullptr.");
231         NAPI_CALL(env, napi_get_undefined(env, &result));
232         return result;
233     }
234     napi_set_named_property(env, objValue, "pixelMap", OHOS::Media::PixelMapNapi::CreatePixelMap(env, param->image));
235     napi_set_named_property(env, objValue, "pickRect", CreateJsRectObject(env, param->imageRect));
236     WLOGFI("pick end");
237     return objValue;
238 }
239 
Resolve(napi_env env, std::unique_ptr<Param> &param)240 napi_value Resolve(napi_env env, std::unique_ptr<Param> &param)
241 {
242     napi_value result;
243     napi_value error;
244     napi_value code;
245     if (param->wret == DmErrorCode::DM_ERROR_INVALID_PARAM) {
246         napi_create_error(env, nullptr, nullptr, &error);
247         napi_create_int32(env, (int32_t)DmErrorCode::DM_ERROR_INVALID_PARAM, &code);
248         napi_set_named_property(env, error, "DM_ERROR_INVALID_PARAM", code);
249         napi_throw(env, error);
250         return error;
251     } else if (param->wret != DmErrorCode::DM_OK) {
252         NAPI_CALL(env, napi_get_undefined(env, &result));
253         return result;
254     }
255     if (param->isPick) {
256         GNAPI_LOG("Resolve Screenshot by picker");
257         return CreateJsPickerObject(env, param);
258     }
259     GNAPI_LOG("Screenshot image Width %{public}d, Height %{public}d",
260         param->image->GetWidth(), param->image->GetHeight());
261     napi_value jsImage = OHOS::Media::PixelMapNapi::CreatePixelMap(env, param->image);
262     return jsImage;
263 }
264 
PickFunc(napi_env env, napi_callback_info info)265 napi_value PickFunc(napi_env env, napi_callback_info info)
266 {
267     GNAPI_LOG("%{public}s called", __PRETTY_FUNCTION__);
268     napi_value argv[1] = { nullptr };  // the max number of input parameters is 1
269     size_t argc = 1;  // the max number of input parameters is 1
270     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
271 
272     auto param = std::make_unique<Param>();
273     if (param == nullptr) {
274         WLOGFE("Create param failed.");
275         return nullptr;
276     }
277     napi_ref ref = nullptr;
278     if (argc == 0) {  // 0 valid parameters
279         GNAPI_LOG("argc == 0");
280         param->validInputParam = true;
281     } else if (GetType(env, argv[0]) == napi_function) {  // 1 valid parameters napi_function
282         GNAPI_LOG("argc >= 1, argv[0]'s type is napi_function");
283         param->validInputParam = true;
284         NAPI_CALL(env, napi_create_reference(env, argv[0], 1, &ref));
285     } else {  // 0 valid parameters
286         GNAPI_LOG("argc == 0");
287         param->validInputParam = true;
288     }
289     param->isPick = true;
290     return AsyncProcess<Param>(env, __PRETTY_FUNCTION__, AsyncGetScreenshot, Resolve, ref, param);
291 }
292 
MainFunc(napi_env env, napi_callback_info info)293 napi_value MainFunc(napi_env env, napi_callback_info info)
294 {
295     GNAPI_LOG("%{public}s called", __PRETTY_FUNCTION__);
296     napi_value argv[2] = {nullptr}; // the max number of input parameters is 2
297     size_t argc = 2; // the max number of input parameters is 2
298     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
299 
300     auto param = std::make_unique<Param>();
301     if (param == nullptr) {
302         WLOGFE("Create param failed.");
303         return nullptr;
304     }
305     param->option.displayId = DisplayManager::GetInstance().GetDefaultDisplayId();
306     napi_ref ref = nullptr;
307     if (argc == 0) { // 0 valid parameters
308         GNAPI_LOG("argc == 0");
309         param->validInputParam = true;
310     } else if (GetType(env, argv[0]) == napi_function) { // 1 valid parameters napi_function
311         GNAPI_LOG("argc >= 1, argv[0]'s type is napi_function");
312         param->validInputParam = true;
313         NAPI_CALL(env, napi_create_reference(env, argv[0], 1, &ref));
314     } else if (GetType(env, argv[0]) == napi_object) {
315         if ((argc >= 2) && (GetType(env, argv[1]) == napi_function)) { // 2 valid parameters napi_object napi_function
316             GNAPI_LOG("argc >= 2, argv[0]'s type is napi_object, argv[1]'s type is napi_function");
317             param->validInputParam = true;
318             param->useInputOption = true;
319             GetScreenshotParam(env, param, argv[0]);
320             NAPI_CALL(env, napi_create_reference(env, argv[1], 1, &ref));
321         } else { // 1 valid parameters napi_object
322             GNAPI_LOG("argc >= 1, argv[0]'s type is napi_object");
323             param->validInputParam = true;
324             param->useInputOption = true;
325             GetScreenshotParam(env, param, argv[0]);
326         }
327     } else { // 0 valid parameters
328         GNAPI_LOG("argc == 0");
329         param->validInputParam = true;
330     }
331     param->isPick = false;
332     return AsyncProcess<Param>(env, __PRETTY_FUNCTION__, AsyncGetScreenshot, Resolve, ref, param);
333 }
334 } // namespace save
335 
SetNamedProperty(napi_env env, napi_value dstObj, const int32_t objValue, const char *propName)336 void SetNamedProperty(napi_env env, napi_value dstObj, const int32_t objValue, const char *propName)
337 {
338     napi_value prop = nullptr;
339     napi_create_int32(env, objValue, &prop);
340     napi_set_named_property(env, dstObj, propName, prop);
341 }
342 
ScreenshotModuleInit(napi_env env, napi_value exports)343 napi_value ScreenshotModuleInit(napi_env env, napi_value exports)
344 {
345     GNAPI_LOG("%{public}s called", __PRETTY_FUNCTION__);
346 
347     napi_value errorCode = nullptr;
348     napi_value dmErrorCode = nullptr;
349     napi_create_object(env, &errorCode);
350     napi_create_object(env, &dmErrorCode);
351 
352     SetNamedProperty(env, errorCode,
353         (int32_t)DMError::DM_ERROR_INIT_DMS_PROXY_LOCKED, "DM_ERROR_INIT_DMS_PROXY_LOCKED");
354     SetNamedProperty(env, errorCode,
355         (int32_t)DMError::DM_ERROR_IPC_FAILED, "DM_ERROR_IPC_FAILED");
356     SetNamedProperty(env, errorCode,
357         (int32_t)DMError::DM_ERROR_REMOTE_CREATE_FAILED, "DM_ERROR_REMOTE_CREATE_FAILED");
358     SetNamedProperty(env, errorCode,
359         (int32_t)DMError::DM_ERROR_NULLPTR, "DM_ERROR_NULLPTR");
360     SetNamedProperty(env, errorCode,
361         (int32_t)DMError::DM_ERROR_INVALID_PARAM, "DM_ERROR_INVALID_PARAM");
362     SetNamedProperty(env, errorCode,
363         (int32_t)DMError::DM_ERROR_WRITE_INTERFACE_TOKEN_FAILED, "DM_ERROR_WRITE_INTERFACE_TOKEN_FAILED");
364     SetNamedProperty(env, errorCode,
365         (int32_t)DMError::DM_ERROR_DEATH_RECIPIENT, "DM_ERROR_DEATH_RECIPIENT");
366     SetNamedProperty(env, errorCode,
367         (int32_t)DMError::DM_ERROR_INVALID_MODE_ID, "DM_ERROR_INVALID_MODE_ID");
368     SetNamedProperty(env, errorCode,
369         (int32_t)DMError::DM_ERROR_WRITE_DATA_FAILED, "DM_ERROR_WRITE_DATA_FAILED");
370     SetNamedProperty(env, errorCode,
371         (int32_t)DMError::DM_ERROR_RENDER_SERVICE_FAILED, "DM_ERROR_RENDER_SERVICE_FAILED");
372     SetNamedProperty(env, errorCode,
373         (int32_t)DMError::DM_ERROR_UNREGISTER_AGENT_FAILED, "DM_ERROR_UNREGISTER_AGENT_FAILED");
374     SetNamedProperty(env, errorCode,
375         (int32_t)DMError::DM_ERROR_INVALID_CALLING, "DM_ERROR_INVALID_CALLING");
376     SetNamedProperty(env, errorCode,
377         (int32_t)DMError::DM_ERROR_UNKNOWN, "DM_ERROR_UNKNOWN");
378 
379     SetNamedProperty(env, dmErrorCode,
380         (int32_t)DmErrorCode::DM_ERROR_NO_PERMISSION, "DM_ERROR_NO_PERMISSION");
381     SetNamedProperty(env, dmErrorCode,
382         (int32_t)DmErrorCode::DM_ERROR_INVALID_PARAM, "DM_ERROR_INVALID_PARAM");
383     SetNamedProperty(env, dmErrorCode,
384         (int32_t)DmErrorCode::DM_ERROR_DEVICE_NOT_SUPPORT, "DM_ERROR_DEVICE_NOT_SUPPORT");
385     SetNamedProperty(env, dmErrorCode,
386         (int32_t)DmErrorCode::DM_ERROR_INVALID_SCREEN, "DM_ERROR_INVALID_SCREEN");
387     SetNamedProperty(env, dmErrorCode,
388         (int32_t)DmErrorCode::DM_ERROR_INVALID_CALLING, "DM_ERROR_INVALID_CALLING");
389     SetNamedProperty(env, dmErrorCode,
390         (int32_t)DmErrorCode::DM_ERROR_SYSTEM_INNORMAL, "DM_ERROR_SYSTEM_INNORMAL");
391 
392     napi_property_descriptor properties[] = {
393         DECLARE_NAPI_FUNCTION("save", save::MainFunc),
394         DECLARE_NAPI_FUNCTION("pick", save::PickFunc),
395         DECLARE_NAPI_PROPERTY("DMError", errorCode),
396         DECLARE_NAPI_PROPERTY("DmErrorCode", dmErrorCode),
397     };
398 
399     NAPI_CALL(env, napi_define_properties(env,
400         exports, sizeof(properties) / sizeof(properties[0]), properties));
401     return exports;
402 }
403 } // namespace OHOS::Rosen
404 
405 static napi_module g_screenshotModule = {
406     .nm_version = 1, // NAPI v1
407     .nm_flags = 0, // normal
408     .nm_filename = nullptr,
409     .nm_register_func = OHOS::Rosen::ScreenshotModuleInit,
410     .nm_modname = "screenshot",
411     .nm_priv = nullptr,
412 };
413 
RegisterModule(void)414 extern "C" __attribute__((constructor)) void RegisterModule(void)
415 {
416     napi_module_register(&g_screenshotModule);
417 }
418