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