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