client/uiclient.c

changeset 942
488178e3e328
child 944
cc23aad6335e
equal deleted inserted replaced
941:e7459e9fbed2 942:488178e3e328
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 }

mercurial