| |
1 /* |
| |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
| |
3 * |
| |
4 * Copyright 2025 Olaf Wintermann. All rights reserved. |
| |
5 * |
| |
6 * Redistribution and use in source and binary forms, with or without |
| |
7 * modification, are permitted provided that the following conditions are met: |
| |
8 * |
| |
9 * 1. Redistributions of source code must retain the above copyright |
| |
10 * notice, this list of conditions and the following disclaimer. |
| |
11 * |
| |
12 * 2. Redistributions in binary form must reproduce the above copyright |
| |
13 * notice, this list of conditions and the following disclaimer in the |
| |
14 * documentation and/or other materials provided with the distribution. |
| |
15 * |
| |
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| |
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| |
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| |
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| |
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| |
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| |
26 * POSSIBILITY OF SUCH DAMAGE. |
| |
27 */ |
| |
28 |
| |
29 #include "uiclient.h" |
| |
30 #include "args.h" |
| |
31 |
| |
32 #include <cx/hash_map.h> |
| |
33 |
| |
34 #include "../ui/common/args.h" |
| |
35 |
| |
36 static MessageHandler *io; |
| |
37 |
| |
38 static CxMap *msg_types; |
| |
39 static CxMap *objects; |
| |
40 |
| |
41 void client_init(MessageHandler *handler) { |
| |
42 io = handler; |
| |
43 |
| |
44 msg_types = cxHashMapCreateSimple(CX_STORE_POINTERS); |
| |
45 cxMapPut(msg_types, "simple_window", msg_simple_window); |
| |
46 cxMapPut(msg_types, "show", msg_show); |
| |
47 |
| |
48 cxMapPut(msg_types, "vbox", msg_vbox); |
| |
49 cxMapPut(msg_types, "hbox", msg_vbox); |
| |
50 cxMapPut(msg_types, "grid", msg_vbox); |
| |
51 cxMapPut(msg_types, "end", msg_end); |
| |
52 cxMapPut(msg_types, "button", msg_button); |
| |
53 |
| |
54 objects = cxHashMapCreateSimple(CX_STORE_POINTERS); |
| |
55 } |
| |
56 |
| |
57 static cxmutstr jsonobj_getstring(const CxJsonValue *obj, const char *name) { |
| |
58 CxJsonValue *value = cxJsonObjGet(obj, name); |
| |
59 if(value->type == CX_JSON_STRING) { |
| |
60 return value->value.string; |
| |
61 } else { |
| |
62 return (cxmutstr){ NULL, 0 }; |
| |
63 } |
| |
64 } |
| |
65 |
| |
66 /* |
| |
67 * UI thread callback |
| |
68 */ |
| |
69 static int msg_received(void *data) { |
| |
70 CxJsonValue *value = data; |
| |
71 if(client_handle_json(NULL, value)) { |
| |
72 fprintf(stderr, "Error: invalid json message\n"); |
| |
73 } |
| |
74 cxJsonValueFree(value); |
| |
75 return 0; |
| |
76 } |
| |
77 |
| |
78 /* |
| |
79 * This message callback is executed in the message handler input thread |
| |
80 */ |
| |
81 void client_msg_received(cxmutstr msg) { |
| |
82 // parse message |
| |
83 CxJson json; |
| |
84 cxJsonInit(&json, NULL); |
| |
85 |
| |
86 cxJsonFilln(&json, msg.ptr, msg.length); |
| |
87 CxJsonValue *value; |
| |
88 if(cxJsonNext(&json, &value) == CX_JSON_NO_ERROR && value) { |
| |
89 // handle json message in the UI thread |
| |
90 ui_call_mainthread(msg_received, value); |
| |
91 } else { |
| |
92 fprintf(stderr, "Error: invalid json message\n"); |
| |
93 } |
| |
94 cxJsonDestroy(&json); |
| |
95 } |
| |
96 |
| |
97 int client_handle_json(UiObject *obj, const CxJsonValue *value) { |
| |
98 if(value->type != CX_JSON_OBJECT) { |
| |
99 return 1; |
| |
100 } |
| |
101 |
| |
102 CxJsonValue *type = cxJsonObjGet(value, "type"); |
| |
103 if(!type || type->type != CX_JSON_STRING) { |
| |
104 return 1; |
| |
105 } |
| |
106 |
| |
107 json_msg_handler handler = cxMapGet(msg_types, type->value.string); |
| |
108 if(!handler) { |
| |
109 return 1; |
| |
110 } |
| |
111 |
| |
112 return handler(obj, value); |
| |
113 } |
| |
114 |
| |
115 int client_handle_children(UiObject *parent, const CxJsonValue *value) { |
| |
116 CxJsonValue *children = cxJsonObjGet(value, "children"); |
| |
117 if(children && children->type == CX_JSON_ARRAY) { |
| |
118 for(int i=0;i<children->value.array.array_size;i++) { |
| |
119 CxJsonValue *child = children->value.array.array[i]; |
| |
120 if(client_handle_json(parent, child)) { |
| |
121 fprintf(stderr, "Error: invalid child\n"); |
| |
122 return 1; |
| |
123 } |
| |
124 } |
| |
125 } |
| |
126 return 0; |
| |
127 } |
| |
128 |
| |
129 void client_add_obj_mapping(UiObject *obj, cxmutstr id) { |
| |
130 cxMapPut(objects, id, obj); |
| |
131 |
| |
132 CxAllocator *a = ui_allocator(obj->ctx); |
| |
133 |
| |
134 WindowData *wdata = cxMalloc(a, sizeof(WindowData)); |
| |
135 wdata->widgets = cxHashMapCreate(a, CX_STORE_POINTERS, 128); |
| |
136 obj->window = wdata; |
| |
137 } |
| |
138 |
| |
139 UiObject* client_get_mapped_obj(cxmutstr id) { |
| |
140 return cxMapGet(objects, id); |
| |
141 } |
| |
142 |
| |
143 void client_reg_widget(UiObject *obj, cxmutstr id, UIWIDGET w) { |
| |
144 WindowData *wdata = obj->window; |
| |
145 if(!wdata) { |
| |
146 fprintf(stderr, "Error: missing obj window data\n"); |
| |
147 return; |
| |
148 } |
| |
149 cxMapPut(wdata->widgets, id, w); |
| |
150 } |
| |
151 |
| |
152 UIWIDGET client_get_widget(UiObject *obj, cxmutstr id) { |
| |
153 WindowData *wdata = obj->window; |
| |
154 if(!wdata) { |
| |
155 fprintf(stderr, "Error: missing obj window data\n"); |
| |
156 return NULL; |
| |
157 } |
| |
158 return cxMapGet(wdata->widgets, id); |
| |
159 } |
| |
160 |
| |
161 static UiObject* get_msg_obj(UiObject *obj, const CxJsonValue *value) { |
| |
162 if(obj) { |
| |
163 return obj; |
| |
164 } |
| |
165 CxJsonValue *obj_id = cxJsonObjGet(value, "obj"); |
| |
166 if(!obj_id || obj_id->type != CX_JSON_STRING) { |
| |
167 return NULL; |
| |
168 } |
| |
169 return client_get_mapped_obj(obj_id->value.string); |
| |
170 } |
| |
171 |
| |
172 int msg_simple_window(UiObject *parent, const CxJsonValue *value) { |
| |
173 cxmutstr id = jsonobj_getstring(value, "id"); |
| |
174 cxmutstr title = jsonobj_getstring(value, "title"); |
| |
175 |
| |
176 if(!id.ptr) { |
| |
177 return 1; |
| |
178 } |
| |
179 |
| |
180 UiObject *obj = ui_simple_window(title.ptr, NULL); |
| |
181 client_add_obj_mapping(obj, id); |
| |
182 |
| |
183 return client_handle_children(obj, value); |
| |
184 } |
| |
185 |
| |
186 int msg_show(UiObject *parent, const CxJsonValue *value) { |
| |
187 UiObject *obj = client_get_mapped_obj(jsonobj_getstring(value, "obj")); |
| |
188 if(!obj) { |
| |
189 return 1; |
| |
190 } |
| |
191 ui_show(obj); |
| |
192 return 0; |
| |
193 } |
| |
194 |
| |
195 typedef UIWIDGET(*ctcreate_func)(UiObject *obj, UiContainerArgs *args); |
| |
196 |
| |
197 static int msg_container(UiObject *parent, const CxJsonValue *value, ctcreate_func create) { |
| |
198 CxJsonValue *args_value = cxJsonObjGet(value, "args"); |
| |
199 cxmutstr id = jsonobj_getstring(value, "id"); |
| |
200 if(!id.ptr) { |
| |
201 return 1; |
| |
202 } |
| |
203 UiObject *obj = get_msg_obj(parent, value); |
| |
204 if(!obj) { |
| |
205 return 1; |
| |
206 } |
| |
207 |
| |
208 UiContainerArgs *args = json2container_args(args_value); |
| |
209 UIWIDGET w = create(obj, args); |
| |
210 ui_container_args_free(args); |
| |
211 client_reg_widget(obj, id, w); |
| |
212 |
| |
213 return 0; |
| |
214 } |
| |
215 |
| |
216 int msg_vbox(UiObject *parent, const CxJsonValue *value) { |
| |
217 return msg_container(parent, value, ui_vbox_create); |
| |
218 } |
| |
219 |
| |
220 int msg_hbox(UiObject *parent, const CxJsonValue *value) { |
| |
221 return msg_container(parent, value, ui_hbox_create); |
| |
222 } |
| |
223 |
| |
224 int msg_grid(UiObject *parent, const CxJsonValue *value) { |
| |
225 return msg_container(parent, value, ui_grid_create); |
| |
226 } |
| |
227 |
| |
228 int msg_end(UiObject *parent, const CxJsonValue *value) { |
| |
229 UiObject *obj = get_msg_obj(parent, value); |
| |
230 if(!obj) { |
| |
231 return 1; |
| |
232 } |
| |
233 ui_end_new(obj); |
| |
234 return 0; |
| |
235 } |
| |
236 |
| |
237 int msg_button(UiObject *parent, const CxJsonValue *value) { |
| |
238 CxJsonValue *args_value = cxJsonObjGet(value, "args"); |
| |
239 cxmutstr id = jsonobj_getstring(value, "id"); |
| |
240 if(!id.ptr) { |
| |
241 return 1; |
| |
242 } |
| |
243 UiObject *obj = get_msg_obj(parent, value); |
| |
244 if(!obj) { |
| |
245 return 1; |
| |
246 } |
| |
247 |
| |
248 UiButtonArgs *args = json2button_args(args_value); |
| |
249 UIWIDGET w = ui_button_create(obj, args); |
| |
250 ui_button_args_free(args); |
| |
251 client_reg_widget(obj, id, w); |
| |
252 |
| |
253 return 0; |
| |
254 } |