Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2014 Red Hat, Inc.
4 : :
5 : : #include <config.h>
6 : :
7 : : #include <cairo.h>
8 : : #include <girepository.h>
9 : :
10 : : #include <js/CallArgs.h>
11 : : #include <js/Conversions.h>
12 : : #include <js/PropertyAndElement.h>
13 : : #include <js/PropertyDescriptor.h> // for JSPROP_READONLY
14 : : #include <js/PropertySpec.h>
15 : : #include <js/RootingAPI.h>
16 : : #include <js/TypeDecls.h>
17 : : #include <js/Value.h>
18 : : #include <jsapi.h> // for JS_NewPlainObject
19 : :
20 : : #include "gi/arg-inl.h"
21 : : #include "gi/arg.h"
22 : : #include "gi/foreign.h"
23 : : #include "gjs/atoms.h"
24 : : #include "gjs/context-private.h"
25 : : #include "gjs/enum-utils.h"
26 : : #include "gjs/jsapi-util-args.h"
27 : : #include "gjs/jsapi-util.h"
28 : : #include "gjs/macros.h"
29 : : #include "modules/cairo-private.h"
30 : :
31 : : GJS_JSAPI_RETURN_CONVENTION
32 : : static bool
33 : : fill_rectangle(JSContext *context,
34 : : JS::HandleObject obj,
35 : : cairo_rectangle_int_t *rect);
36 : :
37 : : #define PRELUDE \
38 : : GJS_GET_THIS(context, argc, vp, argv, obj); \
39 : : cairo_region_t* this_region; \
40 : : if (!CairoRegion::for_js_typecheck(context, obj, &this_region, &argv)) \
41 : : return false;
42 : :
43 : : #define RETURN_STATUS \
44 : : return gjs_cairo_check_status(context, cairo_region_status(this_region), "region");
45 : :
46 : : #define REGION_DEFINE_REGION_FUNC(method) \
47 : : GJS_JSAPI_RETURN_CONVENTION \
48 : : static bool method##_func(JSContext* context, unsigned argc, \
49 : : JS::Value* vp) { \
50 : : PRELUDE; \
51 : : JS::RootedObject other_obj(context); \
52 : : if (!gjs_parse_call_args(context, #method, argv, "o", "other_region", \
53 : : &other_obj)) \
54 : : return false; \
55 : : \
56 : : cairo_region_t* other_region = \
57 : : CairoRegion::for_js(context, other_obj); \
58 : : \
59 : : cairo_region_##method(this_region, other_region); \
60 : : argv.rval().setUndefined(); \
61 : : RETURN_STATUS; \
62 : : }
63 : :
64 : : #define REGION_DEFINE_RECT_FUNC(method) \
65 : : GJS_JSAPI_RETURN_CONVENTION \
66 : : static bool method##_rectangle_func(JSContext* context, unsigned argc, \
67 : : JS::Value* vp) { \
68 : : PRELUDE; \
69 : : JS::RootedObject rect_obj(context); \
70 : : if (!gjs_parse_call_args(context, #method, argv, "o", "rect", \
71 : : &rect_obj)) \
72 : : return false; \
73 : : \
74 : : cairo_rectangle_int_t rect; \
75 : : if (!fill_rectangle(context, rect_obj, &rect)) \
76 : : return false; \
77 : : \
78 : : cairo_region_##method##_rectangle(this_region, &rect); \
79 : : argv.rval().setUndefined(); \
80 : : RETURN_STATUS; \
81 : : }
82 : :
83 [ # # # # : 0 : REGION_DEFINE_REGION_FUNC(union)
# # ]
84 [ # # # # : 0 : REGION_DEFINE_REGION_FUNC(subtract)
# # ]
85 [ # # # # : 0 : REGION_DEFINE_REGION_FUNC(intersect)
# # ]
86 [ # # # # : 0 : REGION_DEFINE_REGION_FUNC(xor)
# # ]
87 : :
88 [ # # # # : 0 : REGION_DEFINE_RECT_FUNC(union)
# # # # ]
89 [ # # # # : 0 : REGION_DEFINE_RECT_FUNC(subtract)
# # # # ]
90 [ # # # # : 0 : REGION_DEFINE_RECT_FUNC(intersect)
# # # # ]
91 [ # # # # : 0 : REGION_DEFINE_RECT_FUNC(xor)
# # # # ]
92 : :
93 : : GJS_JSAPI_RETURN_CONVENTION
94 : : static bool
95 : 0 : fill_rectangle(JSContext *context,
96 : : JS::HandleObject obj,
97 : : cairo_rectangle_int_t *rect)
98 : : {
99 : 0 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
100 : 0 : JS::RootedValue val(context);
101 : :
102 [ # # ]: 0 : if (!JS_GetPropertyById(context, obj, atoms.x(), &val))
103 : 0 : return false;
104 [ # # ]: 0 : if (!JS::ToInt32(context, val, &rect->x))
105 : 0 : return false;
106 : :
107 [ # # ]: 0 : if (!JS_GetPropertyById(context, obj, atoms.y(), &val))
108 : 0 : return false;
109 [ # # ]: 0 : if (!JS::ToInt32(context, val, &rect->y))
110 : 0 : return false;
111 : :
112 [ # # ]: 0 : if (!JS_GetPropertyById(context, obj, atoms.width(), &val))
113 : 0 : return false;
114 [ # # ]: 0 : if (!JS::ToInt32(context, val, &rect->width))
115 : 0 : return false;
116 : :
117 [ # # ]: 0 : if (!JS_GetPropertyById(context, obj, atoms.height(), &val))
118 : 0 : return false;
119 [ # # ]: 0 : if (!JS::ToInt32(context, val, &rect->height))
120 : 0 : return false;
121 : :
122 : 0 : return true;
123 : 0 : }
124 : :
125 : : GJS_JSAPI_RETURN_CONVENTION
126 : : static JSObject *
127 : 0 : make_rectangle(JSContext *context,
128 : : cairo_rectangle_int_t *rect)
129 : : {
130 : 0 : const GjsAtoms& atoms = GjsContextPrivate::atoms(context);
131 : 0 : JS::RootedObject rect_obj(context, JS_NewPlainObject(context));
132 [ # # ]: 0 : if (!rect_obj)
133 : 0 : return nullptr;
134 : 0 : JS::RootedValue val(context);
135 : :
136 : 0 : val = JS::Int32Value(rect->x);
137 [ # # ]: 0 : if (!JS_SetPropertyById(context, rect_obj, atoms.x(), val))
138 : 0 : return nullptr;
139 : :
140 : 0 : val = JS::Int32Value(rect->y);
141 [ # # ]: 0 : if (!JS_SetPropertyById(context, rect_obj, atoms.y(), val))
142 : 0 : return nullptr;
143 : :
144 : 0 : val = JS::Int32Value(rect->width);
145 [ # # ]: 0 : if (!JS_SetPropertyById(context, rect_obj, atoms.width(), val))
146 : 0 : return nullptr;
147 : :
148 : 0 : val = JS::Int32Value(rect->height);
149 [ # # ]: 0 : if (!JS_SetPropertyById(context, rect_obj, atoms.height(), val))
150 : 0 : return nullptr;
151 : :
152 : 0 : return rect_obj;
153 : 0 : }
154 : :
155 : : GJS_JSAPI_RETURN_CONVENTION
156 : : static bool
157 : 0 : num_rectangles_func(JSContext *context,
158 : : unsigned argc,
159 : : JS::Value *vp)
160 : : {
161 [ # # # # ]: 0 : PRELUDE;
162 : : int n_rects;
163 : :
164 [ # # ]: 0 : if (!gjs_parse_call_args(context, "num_rectangles", argv, ""))
165 : 0 : return false;
166 : :
167 : 0 : n_rects = cairo_region_num_rectangles(this_region);
168 : 0 : argv.rval().setInt32(n_rects);
169 : 0 : RETURN_STATUS;
170 : 0 : }
171 : :
172 : : GJS_JSAPI_RETURN_CONVENTION
173 : : static bool
174 : 0 : get_rectangle_func(JSContext *context,
175 : : unsigned argc,
176 : : JS::Value *vp)
177 : : {
178 [ # # # # ]: 0 : PRELUDE;
179 : : int i;
180 : : JSObject *rect_obj;
181 : : cairo_rectangle_int_t rect;
182 : :
183 [ # # ]: 0 : if (!gjs_parse_call_args(context, "get_rectangle", argv, "i",
184 : : "rect", &i))
185 : 0 : return false;
186 : :
187 : 0 : cairo_region_get_rectangle(this_region, i, &rect);
188 : 0 : rect_obj = make_rectangle(context, &rect);
189 : :
190 : 0 : argv.rval().setObjectOrNull(rect_obj);
191 : 0 : RETURN_STATUS;
192 : 0 : }
193 : :
194 : : // clang-format off
195 : : const JSPropertySpec CairoRegion::proto_props[] = {
196 : : JS_STRING_SYM_PS(toStringTag, "Region", JSPROP_READONLY),
197 : : JS_PS_END};
198 : : // clang-format on
199 : :
200 : : const JSFunctionSpec CairoRegion::proto_funcs[] = {
201 : : JS_FN("union", union_func, 0, 0),
202 : : JS_FN("subtract", subtract_func, 0, 0),
203 : : JS_FN("intersect", intersect_func, 0, 0),
204 : : JS_FN("xor", xor_func, 0, 0),
205 : :
206 : : JS_FN("unionRectangle", union_rectangle_func, 0, 0),
207 : : JS_FN("subtractRectangle", subtract_rectangle_func, 0, 0),
208 : : JS_FN("intersectRectangle", intersect_rectangle_func, 0, 0),
209 : : JS_FN("xorRectangle", xor_rectangle_func, 0, 0),
210 : :
211 : : JS_FN("numRectangles", num_rectangles_func, 0, 0),
212 : : JS_FN("getRectangle", get_rectangle_func, 0, 0),
213 : : JS_FS_END};
214 : :
215 : 0 : cairo_region_t* CairoRegion::constructor_impl(JSContext* context,
216 : : const JS::CallArgs& argv) {
217 [ # # ]: 0 : if (!gjs_parse_call_args(context, "Region", argv, ""))
218 : 0 : return nullptr;
219 : :
220 : 0 : return cairo_region_create();
221 : : }
222 : :
223 : 0 : void CairoRegion::finalize_impl(JS::GCContext*, cairo_region_t* region) {
224 [ # # ]: 0 : if (!region)
225 : 0 : return;
226 : :
227 : 0 : cairo_region_destroy(region);
228 : : }
229 : :
230 : 0 : [[nodiscard]] static bool region_to_gi_argument(
231 : : JSContext* context, JS::Value value, const char* arg_name,
232 : : GjsArgumentType argument_type, GITransfer transfer, GjsArgumentFlags flags,
233 : : GIArgument* arg) {
234 [ # # ]: 0 : if (value.isNull()) {
235 [ # # ]: 0 : if (!(flags & GjsArgumentFlags::MAY_BE_NULL)) {
236 : : GjsAutoChar display_name =
237 : 0 : gjs_argument_display_name(arg_name, argument_type);
238 : 0 : gjs_throw(context, "%s may not be null", display_name.get());
239 : 0 : return false;
240 : 0 : }
241 : :
242 : 0 : gjs_arg_unset<void*>(arg);
243 : 0 : return true;
244 : : }
245 : :
246 : 0 : JS::RootedObject obj(context, &value.toObject());
247 : : cairo_region_t *region;
248 : :
249 [ # # ]: 0 : if (!CairoRegion::for_js_typecheck(context, obj, ®ion))
250 : 0 : return false;
251 [ # # ]: 0 : if (transfer == GI_TRANSFER_EVERYTHING)
252 : 0 : cairo_region_destroy(region);
253 : :
254 : 0 : gjs_arg_set(arg, region);
255 : 0 : return true;
256 : 0 : }
257 : :
258 : : GJS_JSAPI_RETURN_CONVENTION
259 : 0 : static bool region_from_gi_argument(JSContext* context,
260 : : JS::MutableHandleValue value_p,
261 : : GIArgument* arg) {
262 : : JSObject* obj =
263 : 0 : CairoRegion::from_c_ptr(context, gjs_arg_get<cairo_region_t*>(arg));
264 [ # # ]: 0 : if (!obj)
265 : 0 : return false;
266 : :
267 : 0 : value_p.setObject(*obj);
268 : 0 : return true;
269 : : }
270 : :
271 : 0 : static bool region_release_argument(JSContext*, GITransfer transfer,
272 : : GIArgument* arg) {
273 [ # # ]: 0 : if (transfer != GI_TRANSFER_NOTHING)
274 : 0 : cairo_region_destroy(gjs_arg_get<cairo_region_t*>(arg));
275 : 0 : return true;
276 : : }
277 : :
278 : :
279 : 2 : void gjs_cairo_region_init(void) {
280 : : static GjsForeignInfo foreign_info = {region_to_gi_argument,
281 : : region_from_gi_argument,
282 : : region_release_argument};
283 : :
284 : 2 : gjs_struct_foreign_register("cairo", "Region", &foreign_info);
285 : 2 : }
|