1 /*
2 * this frameserver represents a complex and partly mischevious client
3 *
4 * testing:
5 * [x] client- supplied borders
6 * [x] client supplied mouse cursor
7 * [x] with conflicting cursorhints
8 * [ ] clipboard
9 * [ ] cut
10 * [ ] spamming text
11 * [ ] images
12 * [ ] audio
13 * [ ] bchunk
14 * [ ] paste
15 * [ ] text
16 * [ ] images
17 * [ ] audio
18 * [ ] bchunk
19 * [ ] accessibility
20 * [ ] debug
21 * [x] titlebar
22 * [x] animated icon, with alert
23 * [ ] popup
24 * [ ] accelerated subsurface
25 * [ ] subwindow- spawn spam
26 * [ ] multiple windows on one buffer
27 * [ ] state load/store on main.
28 * [ ] scrollbar / content feedback support
29 */
30
31 #include <arcan_shmif.h>
32 #include <pthread.h>
33
34 #include "font.h"
35
got_icon(struct arcan_shmif_cont * cont)36 static void got_icon(struct arcan_shmif_cont* cont)
37 {
38 char buf[256];
39 arcan_shmif_enqueue(cont, &(arcan_event){
40 .ext.kind = ARCAN_EVENT(CLOCKREQ), .ext.clock.rate = 20});
41
42 uint8_t gv = 0;
43 draw_box(cont, 0, 0, cont->w, cont->h, SHMIF_RGBA(0, 128, 0, 255));
44 arcan_shmif_signal(cont, SHMIF_SIGVID);
45
46 arcan_event ev;
47 while (arcan_shmif_wait(cont, &ev)){
48 if (ev.category == EVENT_TARGET)
49 switch(ev.tgt.kind){
50 /* synch icon dimensions with displayhint */
51 case TARGET_COMMAND_DISPLAYHINT:
52 arcan_shmif_resize(cont,
53 ev.tgt.ioevs[0].iv, ev.tgt.ioevs[1].iv);
54 break;
55 case TARGET_COMMAND_STEPFRAME:
56 /* repeat "fire-once" event time with some pseudorandom */
57 if (ev.tgt.ioevs[1].iv > 1){
58 arcan_shmif_enqueue(cont, &(arcan_event){
59 .ext.kind = ARCAN_EVENT(CLOCKREQ),
60 .ext.clock.once = true,
61 .ext.clock.rate = 32 + (random() % 100)
62 }
63 );
64 arcan_shmif_enqueue(cont, &(arcan_event){
65 .ext.kind = ARCAN_EVENT(MESSAGE),
66 .ext.message.data = "alert"
67 });
68 }
69 /* for the periodic, just cycle a color */
70 else{
71 gv = (gv + 32) % 127;
72 draw_box(cont,
73 0, 0, cont->w, cont->h, SHMIF_RGBA(0, 128 + gv, 0, 255));
74 arcan_shmif_signal(cont, SHMIF_SIGVID);
75 }
76 break;
77 default:
78 printf("icon event(%s)\n", arcan_shmif_eventstr(&ev, buf, sizeof(buf)));
79 break;
80 }
81 }
82
83 arcan_shmif_drop(cont);
84 free(cont);
85 }
86
got_title(struct arcan_shmif_cont * cont)87 static void got_title(struct arcan_shmif_cont* cont)
88 {
89 draw_box(cont, 0, 0, cont->w, cont->h, SHMIF_RGBA(69, 47, 47, 255));
90 draw_text(cont, "Custom Titlebar", 2, 2, SHMIF_RGBA(255, 255, 255, 255));
91 arcan_shmif_signal(cont, SHMIF_SIGVID);
92
93 /* request new segments inside the titlebar to test if the running appl
94 * ignores more complex hiearchies */
95 arcan_shmif_enqueue(cont, &(arcan_event){
96 .ext.kind = ARCAN_EVENT(SEGREQ),
97 .ext.segreq.kind = SEGID_UNKNOWN
98 });
99 arcan_shmif_enqueue(cont, &(arcan_event){
100 .ext.kind = ARCAN_EVENT(SEGREQ),
101 .ext.segreq.kind = SEGID_TITLEBAR
102 });
103
104 arcan_event ev;
105 while (arcan_shmif_wait(cont, &ev)){
106 if (ev.category == EVENT_TARGET)
107 switch(ev.tgt.kind){
108 /* synch icon dimensions with displayhint */
109 case TARGET_COMMAND_DISPLAYHINT:
110 arcan_shmif_resize(cont,
111 ev.tgt.ioevs[0].iv, ev.tgt.ioevs[1].iv);
112 draw_box(cont, 0, 0, cont->w, cont->h, SHMIF_RGBA(69, 47, 47, 255));
113
114 /* instead of drawing text, update IDENT so the user- set font can be used */
115 arcan_shmif_enqueue(cont, &(arcan_event){
116 .ext.kind = ARCAN_EVENT(IDENT),
117 .ext.message.data = "titlebar- test"
118 });
119 arcan_shmif_signal(cont, SHMIF_SIGVID);
120 break;
121 default:
122 break;
123 }
124 }
125 arcan_shmif_drop(cont);
126 free(cont);
127 }
128
got_clipboard(struct arcan_shmif_cont * cont)129 static void got_clipboard(struct arcan_shmif_cont* cont)
130 {
131 arcan_event ev;
132 char buf[256];
133
134 while (arcan_shmif_wait(cont, &ev)){
135 printf("clipboard(%s)\n", arcan_shmif_eventstr(&ev, buf, sizeof(buf)));
136 }
137 arcan_shmif_drop(cont);
138 free(cont);
139 }
140
got_clipboard_paste(struct arcan_shmif_cont * cont)141 static void got_clipboard_paste(struct arcan_shmif_cont* cont)
142 {
143 arcan_event ev;
144 char buf[256];
145
146 while (arcan_shmif_wait(cont, &ev)){
147 printf("clipboard(%s)\n", arcan_shmif_eventstr(&ev, buf, sizeof(buf)));
148 }
149 arcan_shmif_drop(cont);
150 free(cont);
151 }
152
got_accessibility(struct arcan_shmif_cont * cont)153 static void got_accessibility(struct arcan_shmif_cont* cont)
154 {
155 arcan_event ev;
156 char buf[256];
157
158 while (arcan_shmif_wait(cont, &ev)){
159 printf("accessib.(%s)\n", arcan_shmif_eventstr(&ev, buf, sizeof(buf)));
160 }
161 arcan_shmif_drop(cont);
162 free(cont);
163 }
164
got_cursor(struct arcan_shmif_cont * cont)165 static void got_cursor(struct arcan_shmif_cont* cont)
166 {
167 arcan_event ev;
168 char buf[256];
169
170 draw_box(cont, 0, 0, cont->w, cont->h, SHMIF_RGBA(255, 255, 0, 255));
171 arcan_shmif_signal(cont, SHMIF_SIGVID);
172
173 while (arcan_shmif_wait(cont, &ev)){
174 printf("cursor.(%s)\n", arcan_shmif_eventstr(&ev, buf, sizeof(buf)));
175 }
176 arcan_shmif_drop(cont);
177 free(cont);
178 }
179
got_debug(struct arcan_shmif_cont * cont)180 static void got_debug(struct arcan_shmif_cont* cont)
181 {
182 arcan_event ev;
183 char buf[256];
184
185 while (arcan_shmif_wait(cont, &ev)){
186 printf("debug.(%s)\n", arcan_shmif_eventstr(&ev, buf, sizeof(buf)));
187 }
188 arcan_shmif_drop(cont);
189 free(cont);
190 }
191
update_surf(struct arcan_shmif_cont * cont,size_t border)192 static void update_surf(struct arcan_shmif_cont* cont, size_t border)
193 {
194 if (border)
195 draw_box(cont, 0,0, cont->w, cont->h, SHMIF_RGBA(0x00, 0xff, 0x00, 0xff));
196
197 shmif_pixel* vidp = cont->vidp;
198 for (size_t y = border; y < cont->h - border; y++){
199 float lf_y = (float)y / (cont->h - border - border);
200
201 for (size_t x = border; x < cont->w - border; x++){
202 float lf_x = (float)x / (cont->w - border - border);
203 vidp[y * cont->pitch + x] = SHMIF_RGBA(
204 x*255.0, y*255.0, 0, lf_x * lf_y * 255.0);
205 }
206 }
207
208 arcan_shmif_signal(cont, SHMIF_SIGVID);
209 }
210
211 /*
212 * mapped:
213 * knowned and fixed, reactive = don't request,
214 * threaded = spawn handler in its own thread
215 */
216 struct {
217 bool mapped, reactive, threaded;
218 uint32_t kind;
219 uint32_t id;
220 uint16_t width, height;
221 struct arcan_shmif_cont cont;
222 void (*handler)(struct arcan_shmif_cont* cont);
223 }
224 segtbl[] = {
225 {
226 .kind = SEGID_ICON,
227 .id = 0xfeedface,
228 .handler = got_icon,
229 .width = 64,
230 .height = 64,
231 .threaded = true
232 },
233 {
234 .kind = SEGID_TITLEBAR,
235 .id = 0xbacabaca,
236 .handler = got_title,
237 .threaded = true
238 },
239 {
240 .kind = SEGID_CLIPBOARD,
241 .id = 0x12121212,
242 .handler = got_clipboard,
243 .threaded = true
244 },
245 {
246 .kind = SEGID_CURSOR,
247 .id = 0xdadadada,
248 .handler = got_cursor,
249 .threaded = true
250 },
251 {
252 .kind = SEGID_CLIPBOARD_PASTE,
253 .id = 0x21212121,
254 .handler = got_clipboard_paste,
255 .reactive = true
256 },
257 {
258 .kind = SEGID_DEBUG,
259 .id = 0xacacacac,
260 .handler = got_debug,
261 .reactive = true
262 },
263 {
264 .kind = SEGID_ACCESSIBILITY,
265 .id = 0xcacecace,
266 .reactive = true,
267 .handler = got_accessibility
268 }
269 };
270
271 static const char* cursors[] = {
272 "normal", "wait", "select-inv", "select", "up", "down",
273 "left-right", "drag-up-down", "drag-up", "drag-down", "drag-left",
274 "drag-right", "drag-left-right", "rotate-cw", "rotate-ccw", "normal-tag",
275 "diag-ur", "diag-ll", "drag-diag", "datafield", "move", "typefield",
276 "forbiden", "help", "vertical-datafield", "drag-drop", "drag-reject"
277 };
278 static size_t cursor_ind;
279
main(int argc,char ** argv)280 int main(int argc, char** argv)
281 {
282 struct arg_arr* aarr;
283 size_t border_sz = 0;
284 struct arcan_shmif_cont cont = arcan_shmif_open(
285 SEGID_APPLICATION, SHMIF_ACQUIRE_FATALFAIL, &aarr);
286
287 arcan_shmif_enqueue(&cont, &(arcan_event){
288 .ext.kind = ARCAN_EVENT(CLOCKREQ), .ext.clock.rate = 40});
289
290 /* default static properties to send on connection */
291 struct arcan_event etbl[] = {
292 {
293 .ext.kind = ARCAN_EVENT(IDENT),
294 .ext.message.data = "complex-test"
295 },
296 {
297 .ext.kind = ARCAN_EVENT(COREOPT),
298 .ext.message.data = "0:key:testopt"
299 },
300 {
301 .ext.kind = ARCAN_EVENT(COREOPT),
302 .ext.message.data = "0:descr:some kv option"
303 },
304 {
305 .ext.kind = ARCAN_EVENT(COREOPT),
306 .ext.message.data = "0:arg:val_1|val_2|val_3"
307 },
308 {
309 .ext.kind = ARCAN_EVENT(STATESIZE),
310 .ext.stateinf.size = 4096,
311 .ext.stateinf.type = 0xff
312 },
313 {
314 .ext.kind = ARCAN_EVENT(LABELHINT),
315 .ext.labelhint.label = "SCROLL_UP",
316 .ext.labelhint.idatatype = EVENT_IDATATYPE_DIGITAL
317 },
318 {
319 .ext.kind = ARCAN_EVENT(LABELHINT),
320 .ext.labelhint.label = "SCROLL_DOWN",
321 .ext.labelhint.idatatype = EVENT_IDATATYPE_DIGITAL
322 },
323 };
324
325 arcan_shmif_resize(&cont, 640, 480);
326
327 /* request all 'special types' */
328 for (size_t i = 0; i < sizeof(segtbl)/sizeof(segtbl[0]); i++){
329 if (!segtbl[i].reactive)
330 arcan_shmif_enqueue(&cont, &(arcan_event) {
331 .ext.kind = ARCAN_EVENT(SEGREQ),
332 .ext.segreq.kind = segtbl[i].kind,
333 .ext.segreq.id = segtbl[i].id,
334 .ext.segreq.width = segtbl[i].width,
335 .ext.segreq.height = segtbl[i].height
336 });
337 }
338
339 /* define viewport that takes border into account */
340 arcan_shmif_enqueue(&cont, &(arcan_event){
341 .ext.kind = ARCAN_EVENT(VIEWPORT),
342 .ext.viewport = {.w = 640, .h = 480, .border = 5}
343 });
344
345 for (size_t i = 0; i < sizeof(etbl)/sizeof(etbl[0]); i++)
346 arcan_shmif_enqueue(&cont, &etbl[i]);
347
348 /* draw border and hint that it is used */
349 arcan_event ev;
350 while(arcan_shmif_wait(&cont, &ev) != 0){
351 if (ev.category == EVENT_TARGET)
352 switch(ev.tgt.kind){
353 case TARGET_COMMAND_NEWSEGMENT:
354 for (size_t i = 0; i < sizeof(segtbl)/sizeof(segtbl[0]); i++){
355 /* map if req-id matches or (type+reactive) */
356 if (segtbl[i].id == ev.tgt.ioevs[0].iv ||
357 (segtbl[i].reactive && ev.tgt.ioevs[2].iv == segtbl[i].kind)){
358 struct arcan_shmif_cont* tc = malloc(sizeof(struct arcan_shmif_cont));
359 *tc = arcan_shmif_acquire(&cont,
360 NULL, segtbl[i].kind, SHMIF_DISABLE_GUARD);
361 if (!tc->vidp)
362 free(tc);
363 else {
364 pthread_t pth;
365 pthread_create(&pth, NULL, (void*) segtbl[i].handler, tc);
366 pthread_detach(pth);
367 }
368 }
369 }
370 break;
371 case TARGET_COMMAND_STEPFRAME:
372 /* on timer, rotate custom mouse cursor and change border area */
373 if (ev.tgt.ioevs[1].iv > 1){
374 static bool cflip;
375 arcan_event ev = {.ext.kind = ARCAN_EVENT(CURSORHINT)};
376 cflip = !cflip;
377 if (cflip)
378 memcpy(ev.ext.message.data, "custom", 6);
379 else{
380 memcpy(ev.ext.message.data,
381 cursors[cursor_ind], strlen(cursors[cursor_ind]));
382 cursor_ind = (cursor_ind+1) % (sizeof(cursors)/sizeof(cursors[0]));
383 }
384 }
385 border_sz = (border_sz + 2) % 10;
386 arcan_shmif_enqueue(&cont, &ev);
387 arcan_shmif_enqueue(&cont, &(arcan_event){
388 .ext.kind = ARCAN_EVENT(VIEWPORT),
389 .ext.viewport.w = cont.w,
390 .ext.viewport.h = cont.h,
391 .ext.viewport.border = border_sz
392 });
393 update_surf(&cont, border_sz);
394 break;
395
396 default:
397 break;
398 }
399 else if (ev.category == EVENT_IO){
400 /* mouse cursor implementation, use position to send update to cursor
401 * subsegment (if present), right click in one quanta to set popup.. these are
402 * expensive on purpose . Scroll UP ->update content hint, Scroll Down ->
403 * update content hint */
404 }
405 else
406 ;
407 /* if we receive the proper segment request, forward to the tbl */
408 /* send CURSORHINT with cycle from the list */
409 }
410 arcan_shmif_drop(&cont);
411 return EXIT_SUCCESS;
412 }
413