1 /*
2  * Copyright 2003-2020, Björn Ståhl
3  * License: 3-Clause BSD, see COPYING file in arcan source repository.
4  * Reference: http://arcan-fe.com
5  */
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <inttypes.h>
12 #include <stdbool.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <math.h>
16 #include <assert.h>
17 #include <limits.h>
18 #include <setjmp.h>
19 
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 
23 #include "arcan_math.h"
24 #include "arcan_general.h"
25 #include "arcan_shmif.h"
26 #include "arcan_shmif_sub.h"
27 #include "arcan_event.h"
28 #include "arcan_video.h"
29 #include "arcan_videoint.h"
30 #include "arcan_audio.h"
31 #include "arcan_audioint.h"
32 #include "arcan_renderfun.h"
33 
34 #define FRAMESERVER_PRIVATE
35 #include "arcan_frameserver.h"
36 #include "arcan_conductor.h"
37 
38 #include "arcan_event.h"
39 #include "arcan_img.h"
40 
41 /* temporary workaround while migrating */
42 typedef struct TTF_Font TTF_Font;
43 #include "../shmif/tui/raster/raster.h"
44 
45 /*
46  * implementation defined for out-of-order execution
47  * and reordering protection
48  */
49 #ifndef FORCE_SYNCH
50 	#define FORCE_SYNCH() {\
51 		asm volatile("": : :"memory");\
52 		__sync_synchronize();\
53 	}
54 #endif
55 
56 static int g_buffers_locked;
57 
58 static inline void emit_deliveredframe(arcan_frameserver* src,
59 	unsigned long long pts, unsigned long long framecount);
60 static inline void emit_droppedframe(arcan_frameserver* src,
61 	unsigned long long pts, unsigned long long framecount);
62 
autoclock_frame(arcan_frameserver * tgt)63 static void autoclock_frame(arcan_frameserver* tgt)
64 {
65 	if (!tgt->clock.left)
66 		return;
67 
68 	if (!tgt->clock.frametime)
69 		tgt->clock.frametime = arcan_frametime();
70 
71 	int64_t delta = arcan_frametime() - tgt->clock.frametime;
72 
73 /* something horribly wrong, rebase */
74 	if (delta < 0){
75 		tgt->clock.frametime = arcan_frametime();
76 		return;
77 	}
78 	else if (delta == 0)
79 		return;
80 
81 /* this is a course-grained optimistic 'wait at least n' timer */
82 	if (tgt->clock.left <= delta){
83 		tgt->clock.left = tgt->clock.start;
84 		tgt->clock.frametime = arcan_frametime();
85 		arcan_event ev = {
86 			.category = EVENT_TARGET,
87 			.tgt.kind = TARGET_COMMAND_STEPFRAME,
88 			.tgt.ioevs[0].iv = delta / tgt->clock.start,
89 			.tgt.ioevs[1].iv = 1
90 		};
91 		platform_fsrv_pushevent(tgt, &ev);
92 	}
93 	else
94 		tgt->clock.left -= delta;
95 }
96 
arcan_frameserver_free(arcan_frameserver * src)97 arcan_errc arcan_frameserver_free(arcan_frameserver* src)
98 {
99 	if (!src)
100 		return ARCAN_ERRC_NO_SUCH_OBJECT;
101 
102 	if (src->fused){
103 		src->fuse_blown = true;
104 		return ARCAN_OK;
105 	}
106 
107 	arcan_conductor_deregister_frameserver(src);
108 	arcan_frameserver_close_bufferqueues(src, true, true);
109 
110 	arcan_aobj_id aid = src->aid;
111 	uintptr_t tag = src->tag;
112 	arcan_vobj_id vid = src->vid;
113 
114 /* unhook audio monitors */
115 	arcan_aobj_id* base = src->alocks;
116 	while (base && *base){
117 		arcan_audio_hookfeed(*base, NULL, NULL, NULL);
118 		base++;
119 	}
120 	src->alocks = NULL;
121 
122 /* release the font group as well, this has the side effect of a 'pacify-target'
123  * call where the frameserver is transformed to a normal video object - no
124  * being drawable or responding to font size changes (as font state is lost glyph
125  * caches can't be rebuilt or used) - something to reconsider when we can do
126  * shared atlases */
127 	arcan_renderfun_release_fontgroup(src->desc.text.group);
128 	src->desc.text.group = NULL;
129 
130 	char msg[32];
131 
132 	if (src->cookie_fail)
133 		snprintf(msg, COUNT_OF(msg), "Integrity cookie mismatch");
134 	else if (!platform_fsrv_lastwords(src, msg, COUNT_OF(msg)))
135 		snprintf(msg, COUNT_OF(msg), "Couldn't access metadata (SIGBUS?)");
136 
137 /* will free, so no UAF here - only time the function returns false is when we
138  * are somehow running it twice one the same src */
139 	if (!platform_fsrv_destroy(src))
140 		return ARCAN_ERRC_UNACCEPTED_STATE;
141 
142 	arcan_audio_stop(aid);
143 
144 /* make sure there is no other weird dangling state around and forward the
145  * client 'last words' as a troubleshooting exit 'status' */
146 	vfunc_state emptys = {0};
147 	arcan_video_alterfeed(vid, FFUNC_NULL, emptys);
148 
149 	arcan_event sevent = {
150 		.category = EVENT_FSRV,
151 		.fsrv.kind = EVENT_FSRV_TERMINATED,
152 		.fsrv.video = vid,
153 		.fsrv.glsource = false,
154 		.fsrv.audio = aid,
155 		.fsrv.otag = tag
156 	};
157 
158 	memcpy(&sevent.fsrv.message, msg, COUNT_OF(msg));
159 	arcan_event_enqueue(arcan_event_defaultctx(), &sevent);
160 
161 	return ARCAN_OK;
162 }
163 
164 /*
165  * specialized case, during recovery and adoption the lose association between
166  * tgt->parent (vid) is broken as the vid may in some rare cases be reassigned
167  * when there's context namespace collisions.
168  */
default_adoph(arcan_frameserver * tgt,arcan_vobj_id id)169 static void default_adoph(arcan_frameserver* tgt, arcan_vobj_id id)
170 {
171 	tgt->parent.vid = arcan_video_findstate(
172 		ARCAN_TAG_FRAMESERV, tgt->parent.ptr);
173 	tgt->vid = id;
174 }
175 
arcan_frameserver_control_chld(arcan_frameserver * src)176 bool arcan_frameserver_control_chld(arcan_frameserver* src){
177 /* bunch of terminating conditions -- frameserver messes with the structure to
178  * provoke a vulnerability, frameserver dying or timing out, ... */
179 	bool cookie_match = true;
180 	bool alive = src->flags.alive && src->shm.ptr && src->shm.ptr->dms;
181 
182 /* specifically track the cookie failing issue so that we can forward that as an
183  * important failure reason since it is indicative of something more than just a
184  * buggy client */
185 	if (alive){
186 		alive = platform_fsrv_validchild(src);
187 		if (alive && src->shm.ptr->cookie != arcan_shmif_cookie()){
188 			src->cookie_fail = true;
189 			alive = false;
190 		}
191 	}
192 
193 /* subsegment may well be alive when the parent has just died, thus we need to
194  * check the state of the parent and if it is dead, clean up just the same,
195  * which will likely lead to kill() and a cascade */
196 	if (alive && src->parent.vid != ARCAN_EID){
197 		arcan_vobject* vobj = arcan_video_getobject(src->parent.vid);
198 		if (!vobj || vobj->feed.state.tag != ARCAN_TAG_FRAMESERV)
199 			alive = false;
200 	}
201 
202 	if (!alive){
203 /*
204  * This one is really ugly and kept around here as a note. The previous idea
205  * was that there might still be relevant content in the queue, e.g. descriptor
206  * transfers etc. that one might want to preserve, but combined with the drain-
207  * enqueue approach to deal with queue saturation and high-priority event like
208  * frameserver could lead to an order issue where there are EXTERNAL_ events
209  * on the queue after the corresponding frameserver has been terminated, leading
210  * to a possible invalid-tag deref into scripting WM.
211  *
212 		arcan_event_queuetransfer(
213 			arcan_event_defaultctx(), &src->inqueue, src->queue_mask, 0.5, src);
214  */
215 
216 		arcan_frameserver_free(src);
217 		return false;
218 	}
219 
220 	return true;
221 }
222 
arcan_frameserver_close_bufferqueues(arcan_frameserver * src,bool incoming,bool pending)223 void arcan_frameserver_close_bufferqueues(
224 	arcan_frameserver* src, bool incoming, bool pending)
225 {
226 	if (!src)
227 		return;
228 
229 	if (incoming){
230 		for (size_t i = 0; i < src->vstream.incoming_used; i++){
231 			if (src->vstream.incoming[i].fd > 0){
232 				close(src->vstream.incoming[i].fd);
233 				src->vstream.incoming[i].fd = -1;
234 			}
235 		}
236 		src->vstream.incoming_used = 0;
237 	}
238 
239 	if (pending){
240 		for (size_t i = 0; i < src->vstream.pending_used; i++){
241 			if (src->vstream.pending[i].fd > 0){
242 				close(src->vstream.pending[i].fd);
243 				src->vstream.pending[i].fd = -1;
244 			}
245 		}
246 		src->vstream.pending_used = 0;
247 	}
248 }
249 
push_buffer(arcan_frameserver * src,struct agp_vstore * store,struct arcan_shmif_region * dirty)250 static bool push_buffer(arcan_frameserver* src,
251 	struct agp_vstore* store, struct arcan_shmif_region* dirty)
252 {
253 	struct stream_meta stream = {.buf = NULL};
254 	bool explicit = src->flags.explicit;
255 
256 /* we know that vpending contains the latest region that was synched,
257  * so the ~vready mask should be the bits that we want to keep. */
258 	int vready = atomic_load_explicit(&src->shm.ptr->vready,memory_order_consume);
259 	int vmask=~atomic_load_explicit(&src->shm.ptr->vpending,memory_order_consume);
260 
261 	TRACE_MARK_ONESHOT("frameserver", "buffer-eval", TRACE_SYS_DEFAULT, src->vid, vready, "");
262 
263 	vready = (vready <= 0 || vready > src->vbuf_cnt) ? 0 : vready - 1;
264 	shmif_pixel* buf = src->vbufs[vready];
265 
266 /* Need to do this check here as-well as in the regular frameserver tick
267  * control because the backing store might have changed somehwere else. */
268 	if (src->desc.width != store->w || src->desc.height != store->h ||
269 		src->desc.hints != src->desc.pending_hints || src->desc.rz_flag){
270 		src->desc.hints = src->desc.pending_hints;
271 
272 		TRACE_MARK_ONESHOT("frameserver", "buffer-resize", TRACE_SYS_DEFAULT,
273 			src->vid, src->desc.width * src->desc.height, "");
274 
275 		arcan_event rezev = {
276 			.category = EVENT_FSRV,
277 			.fsrv.kind = EVENT_FSRV_RESIZED,
278 			.fsrv.width = src->desc.width,
279 			.fsrv.height = src->desc.height,
280 			.fsrv.video = src->vid,
281 			.fsrv.audio = src->aid,
282 			.fsrv.otag = src->tag,
283 			.fsrv.glsource = src->desc.hints & SHMIF_RHINT_ORIGO_LL
284 		};
285 
286 /* Manually enabled mode where the WM side wants access to the resized buffer
287  * but also wants to keep the client locked and waiting. */
288 		if (src->flags.rz_ack){
289 			if (src->rz_known == 0){
290 				arcan_event_enqueue(arcan_event_defaultctx(), &rezev);
291 				src->rz_known = 1;
292 				return false;
293 			}
294 /* still no response, wait */
295 			else if (src->rz_known == 1){
296 				return false;
297 			}
298 /* ack:ed, continue with resize */
299 			else
300 				src->rz_known = 0;
301 		}
302 		else
303 			arcan_event_enqueue(arcan_event_defaultctx(), &rezev);
304 
305 		store->vinf.text.d_fmt = (src->desc.hints & SHMIF_RHINT_IGNORE_ALPHA) ||
306 			src->flags.no_alpha_copy ? GL_NOALPHA_PIXEL_FORMAT : GL_STORE_PIXEL_FORMAT;
307 
308 /* this might not take if the store is locked - i.e. GPU resources will not
309  * match local copies, the main context where that matters is if the vobj is
310  * mapped to an output display and thus has a fixed buffer resolution */
311 		arcan_video_resizefeed(src->vid, src->desc.width, src->desc.height);
312 
313 		src->desc.rz_flag = false;
314 		explicit = true;
315 	}
316 
317 /* special case, the contents is in a compressed format that can either be
318  * rasterized or deferred to on-GPU rasterization / atlas lookup, so the other
319  * setup isn't strictly needed. */
320 	if (src->desc.hints & SHMIF_RHINT_TPACK){
321 		TRACE_MARK_ENTER("frameserver", "buffer-tpack-raster", TRACE_SYS_DEFAULT, src->vid, 0, "");
322 
323 /* if the font-group is broken (no hints, ...), set a bitmap only one
324  * as well as calculate cell dimensions accordingly */
325 		if (!src->desc.text.group){
326 			src->desc.text.group = arcan_renderfun_fontgroup(NULL, 0);
327 			arcan_renderfun_fontgroup_size(src->desc.text.group,
328 				0, 0, &src->desc.text.cellw, &src->desc.text.cellh);
329 		}
330 
331 /* raster is 'built' every update from whatever caching mechanism is in
332  * renderfun, it is only valid for the tui_raster_renderagp call as the
333  * contents can be invalidated with any resize/font-size/font change. */
334 		struct tui_raster_context* raster =
335 			arcan_renderfun_fontraster(src->desc.text.group);
336 
337 /* This is the next step to change, of course we should merge the buffers into
338  * a tpack_vstore and then use normal txcos etc. to pick our visible set, and a
339  * MSDF text atlas to get drawing lists, removing the last 'big buffer'
340  * requirement, as well as drawing the cursor separately. */
341 		tui_raster_renderagp(raster, store, (uint8_t*) buf,
342 			src->desc.width * src->desc.height * sizeof(shmif_pixel), &stream);
343 
344 /* Raster failed for some reason - tactics would be to send reset and after
345  * n- fails kill it for not complying with format - something to finish when
346  * we have the atlas bits in place, and have a DEBUG+dump buffer version */
347 		if (!stream.buf){
348 			arcan_warning("client-tpack() - couldn't raster buffer\n");
349 			goto commit_mask;
350 		}
351 
352 /* The dst-copy is also a hack / problematic in that way - the invalidation
353  * should really be handled in some other way */
354 		if (store->dst_copy){
355 			struct agp_region reg = {
356 				.x1 = stream.x1,
357 				.y1 = stream.y1,
358 				.x2 = stream.x1 + stream.w,
359 				.y2 = stream.y1 + stream.h
360 			};
361 			agp_vstore_copyreg(store,
362 				store->dst_copy, reg.x1, reg.y1, reg.x2, reg.y2);
363 			platform_video_invalidate_map(store->dst_copy, reg);
364 		}
365 
366 		stream = agp_stream_prepare(store, stream, STREAM_RAW_DIRECT);
367 		agp_stream_commit(store, stream);
368 
369 /* Return feedback on kerning in px. Set the entire buffer regardless of delta
370  * since when we get an actual kerning table in the vstore - it will be cheaper
371  * with an aligned (rows * cols) memcpy than to jump around and patch in bytes
372  * on the lines that have changed and to have the client do the same thing. */
373 		size_t i = 0;
374 		if (!src->desc.height || !src->desc.text.cellh){
375 			TRACE_MARK_EXIT("frameserver",
376 				"buffer-tpack-raster", TRACE_SYS_WARN, src->vid, 0, "invalid tpack size");
377 			goto commit_mask;
378 		}
379 
380 		size_t n_rows = src->desc.height / src->desc.text.cellh;
381 		size_t n_cols = src->desc.width / src->desc.text.cellw;
382 		size_t n_cells = n_rows * n_cols;
383 
384 /* Size is guaranteed to be >= w * h * tui_cell_size + line_hdr * h + static header */
385 		memset(buf, src->desc.text.cellw, n_cells);
386 		buf[n_cells] = 0xff;
387 
388 		TRACE_MARK_EXIT("frameserver", "buffer-tpack-raster", TRACE_SYS_DEFAULT, src->vid, 0, "");
389 		goto commit_mask;
390 	}
391 
392 	if (src->vstream.pending_used){
393 		bool failev = src->vstream.dead;
394 
395 		if (!failev){
396 /* mapping and bound checking is done inside of arcan_event.c */
397 			memcpy(stream.planes, src->vstream.pending,
398 				sizeof(struct agp_buffer_plane) * src->vstream.pending_used);
399 			stream.used = src->vstream.pending_used;
400 			stream = agp_stream_prepare(store, stream, STREAM_HANDLE);
401 			src->vstream.pending_used = 0;
402 
403 /* the vstream can die because of a format mismatch, platform validation failure
404  * or triggered by manually disabling it for this frameserver */
405 			failev = !stream.state;
406 		}
407 
408 /* buffer passing failed, mark that as an unsupported mode for some reason, log
409  * and send back to client to revert to shared memory and extra copies */
410 		if (failev){
411 			arcan_event ev = {
412 				.category = EVENT_TARGET,
413 				.tgt.kind = TARGET_COMMAND_BUFFER_FAIL
414 			};
415 
416 /* handle format should be abstracted to platform, but right now it is just
417  * mapped as a file-descriptor, if iostreams or windows is reintroduced, this
418  * will need to be fixed */
419 			arcan_event_enqueue(&src->outqueue, &ev);
420 			arcan_frameserver_close_bufferqueues(src, true, true);
421 			src->vstream.dead = true;
422 
423 			TRACE_MARK_ONESHOT("frameserver", "buffer-handle", TRACE_SYS_WARN, src->vid, 0, "platform reject");
424 		}
425 		else
426 			agp_stream_commit(store, stream);
427 
428 		goto commit_mask;
429 	}
430 
431 	stream.buf = buf;
432 /* validate, fallback to fullsynch if we get bad values */
433 
434 	if (dirty){
435 		stream.x1 = dirty->x1; stream.w = dirty->x2 - dirty->x1;
436 		stream.y1 = dirty->y1; stream.h = dirty->y2 - dirty->y1;
437 		stream.dirty = /* unsigned but int prom. */
438 			(dirty->x2 - dirty->x1 > 0 && stream.w <= store->w) &&
439 			(dirty->y2 - dirty->y1 > 0 && stream.h <= store->h);
440 		src->desc.region = *dirty;
441 		src->desc.region_valid = true;
442 	}
443 	else
444 		src->desc.region_valid = false;
445 
446 /* perhaps also convert hints to message string */
447 	size_t n_px = stream.w * stream.h;
448 	TRACE_MARK_ENTER("frameserver", "buffer-upload", TRACE_SYS_DEFAULT, src->vid, n_px, "");
449 
450 	stream = agp_stream_prepare(store, stream, explicit ?
451 		STREAM_RAW_DIRECT_SYNCHRONOUS : (
452 			src->flags.local_copy ? STREAM_RAW_DIRECT_COPY : STREAM_RAW_DIRECT));
453 
454 	agp_stream_commit(store, stream);
455 	TRACE_MARK_EXIT("frameserver", "buffer-upload", TRACE_SYS_DEFAULT, src->vid, n_px, "upload");
456 
457 commit_mask:
458 	atomic_fetch_and(&src->shm.ptr->vpending, vmask);
459 	TRACE_MARK_ONESHOT("frameserver", "buffer-release", TRACE_SYS_DEFAULT, src->vid, vmask, "release");
460 	return true;
461 }
462 
463 enum arcan_ffunc_rv arcan_frameserver_nullfeed FFUNC_HEAD
464 {
465 	arcan_frameserver* tgt = state.ptr;
466 
467 	if (!tgt || state.tag != ARCAN_TAG_FRAMESERV)
468 		return FRV_NOFRAME;
469 
470 	TRAMP_GUARD(FRV_NOFRAME, tgt);
471 
472 	if (cmd == FFUNC_DESTROY)
473 		arcan_frameserver_free(state.ptr);
474 
475 	else if (cmd == FFUNC_ADOPT)
476 		default_adoph(tgt, srcid);
477 
478 	else if (cmd == FFUNC_TICK){
479 		if (!arcan_frameserver_control_chld(tgt))
480 			goto no_out;
481 
482 		arcan_event_queuetransfer(
483 			arcan_event_defaultctx(), &tgt->inqueue, tgt->queue_mask, 0.5, tgt);
484 	}
485 	else if (cmd == FFUNC_DESTROY)
486 		arcan_frameserver_free(tgt);
487 
488 no_out:
489 	platform_fsrv_leave();
490 	return FRV_NOFRAME;
491 }
492 
493 enum arcan_ffunc_rv arcan_frameserver_pollffunc FFUNC_HEAD
494 {
495 	arcan_frameserver* tgt = state.ptr;
496 	struct arcan_shmif_page* shmpage = tgt->shm.ptr;
497 	bool term;
498 
499 	if (state.tag != ARCAN_TAG_FRAMESERV || !shmpage){
500 		arcan_warning("platform/posix/frameserver.c:socketpoll, called with"
501 			" invalid source tag, investigate.\n");
502 		return FRV_NOFRAME;
503 	}
504 
505 /* wait for connection, then unlink directory node and switch to verify. */
506 	switch (cmd){
507 	case FFUNC_POLL:{
508 		int sc = platform_fsrv_socketpoll(tgt);
509 		if (sc == -1){
510 /* will yield terminate, close the socket etc. and propagate the event */
511 			if (errno == EBADF){
512 				arcan_frameserver_free(tgt);
513 			}
514 			return FRV_NOFRAME;
515 		}
516 
517 		arcan_video_alterfeed(tgt->vid, FFUNC_SOCKVER, state);
518 
519 /* this is slightly special, we want to allow the option to re-use the
520  * listening point descriptor to avoid some problems from the bind/accept stage
521  * that could cause pending connections to time out etc. even though the
522  * scripting layer wants to reuse the connection point. To deal with this
523  * problem, we keep the domain socket open (sc) and forward to the scripting
524  * layer so that it may use it as an argument to listen_external (or close it).
525  * */
526 		arcan_event adopt = {
527 			.category = EVENT_FSRV,
528 			.fsrv.kind = EVENT_FSRV_EXTCONN,
529 			.fsrv.descriptor = sc,
530 			.fsrv.otag = tgt->tag,
531 			.fsrv.video = tgt->vid
532 		};
533 		snprintf(adopt.fsrv.ident, sizeof(adopt.fsrv.ident)/
534 			sizeof(adopt.fsrv.ident[0]), "%s", tgt->sockkey);
535 
536 		arcan_event_enqueue(arcan_event_defaultctx(), &adopt);
537 
538 		return arcan_frameserver_verifyffunc(
539 			cmd, buf, buf_sz, width, height, mode, state, tgt->vid);
540 	}
541 	break;
542 
543 /* socket is closed in frameserver_destroy */
544 	case FFUNC_DESTROY:
545 		arcan_frameserver_free(tgt);
546 	break;
547 	default:
548 	break;
549 	}
550 
551 	return FRV_NOFRAME;
552 }
553 
554 enum arcan_ffunc_rv arcan_frameserver_verifyffunc FFUNC_HEAD
555 {
556 	arcan_frameserver* tgt = state.ptr;
557 	char ch = '\n';
558 	bool term;
559 	size_t ntw;
560 
561 	switch (cmd){
562 /* authentication runs one byte at a time over a call barrier, the function
563  * will process the entire key even when they start mismatching. LTO could
564  * still try and optimize this and become a timing oracle, but havn't been able
565  * to. */
566 	case FFUNC_POLL:
567 		while (-1 == platform_fsrv_socketauth(tgt)){
568 			if (errno == EBADF){
569 				arcan_frameserver_free(tgt);
570 				return FRV_NOFRAME;
571 			}
572 			else if (errno == EWOULDBLOCK){
573 				return FRV_NOFRAME;
574 			}
575 		}
576 /* connection is authenticated, switch feed to the normal 'null until
577  * first refresh' and try it. */
578 		arcan_video_alterfeed(tgt->vid, FFUNC_NULLFRAME, state);
579 		arcan_errc errc;
580 		tgt->aid = arcan_audio_feed((arcan_afunc_cb)
581 			arcan_frameserver_audioframe_direct, tgt, &errc);
582 		tgt->sz_audb = 0;
583 		tgt->ofs_audb = 0;
584 		tgt->audb = NULL;
585 /* no point in trying the frame- poll this round, the odds of the other side
586  * preempting us, mapping and populating the buffer etc. are not realistic */
587 		return FRV_NOFRAME;
588 	break;
589 	case FFUNC_DESTROY:
590 		arcan_frameserver_free(tgt);
591 	break;
592 	default:
593 	break;
594 	}
595 
596 	return FRV_NOFRAME;
597 }
598 
599 /* mainly used for VFRAME- events on full queue so that the client isn't
600  * stuck waiting if it only clocks based on STEPFRAME rather than vready */
flush_queued(arcan_frameserver * tgt)601 static void flush_queued(arcan_frameserver* tgt)
602 {
603 	size_t torem = 0;
604 	for (size_t i = 0; i < tgt->n_pending; i++){
605 		if (ARCAN_OK != platform_fsrv_pushevent(tgt, &tgt->pending_queue[i]))
606 			return;
607 		torem++;
608 	}
609 
610 /* full dequeue? */
611 	if (torem == tgt->n_pending){
612 		tgt->n_pending = 0;
613 		return;
614 	}
615 
616 /* otherwise partial, move */
617 	tgt->n_pending = tgt->n_pending - torem;
618 	memmove(tgt->pending_queue, &tgt->pending_queue[torem],
619 		sizeof(struct arcan_event) * tgt->n_pending);
620 }
621 
622 enum arcan_ffunc_rv arcan_frameserver_emptyframe FFUNC_HEAD
623 {
624 	arcan_frameserver* tgt = state.ptr;
625 	if (!tgt || state.tag != ARCAN_TAG_FRAMESERV)
626 		return FRV_NOFRAME;
627 
628 	TRAMP_GUARD(FRV_NOFRAME, tgt);
629 
630 	switch (cmd){
631 		case FFUNC_POLL:
632 			if (tgt->shm.ptr->resized){
633 				if (arcan_frameserver_tick_control(tgt, false, FFUNC_VFRAME) &&
634 					tgt->shm.ptr && tgt->shm.ptr->vready){
635 					platform_fsrv_leave();
636 					return FRV_GOTFRAME;
637 				}
638 			}
639 
640 			if (tgt->n_pending)
641 				flush_queued(tgt);
642 
643 			if (tgt->flags.autoclock && tgt->clock.frame)
644 				autoclock_frame(tgt);
645 		break;
646 
647 		case FFUNC_TICK:
648 			arcan_frameserver_tick_control(tgt, true, FFUNC_VFRAME);
649 		break;
650 
651 		case FFUNC_DESTROY:
652 			arcan_frameserver_free(tgt);
653 		break;
654 
655 		case FFUNC_ADOPT:
656 			default_adoph(tgt, srcid);
657 		break;
658 
659 		default:
660 		break;
661 	}
662 
663 	platform_fsrv_leave();
664 	return FRV_NOFRAME;
665 }
666 
arcan_frameserver_lock_buffers(int state)667 void arcan_frameserver_lock_buffers(int state)
668 {
669 	g_buffers_locked = state;
670 }
671 
arcan_frameserver_releaselock(struct arcan_frameserver * tgt)672 int arcan_frameserver_releaselock(struct arcan_frameserver* tgt)
673 {
674 	if (!tgt->flags.release_pending || !tgt->shm.ptr){
675 		return 0;
676 	}
677 
678 	tgt->flags.release_pending = false;
679 	TRAMP_GUARD(0, tgt);
680 
681 	atomic_store_explicit(&tgt->shm.ptr->vready, 0, memory_order_release);
682 	arcan_sem_post( tgt->vsync );
683 		if (tgt->desc.hints & SHMIF_RHINT_VSIGNAL_EV){
684 			TRACE_MARK_ONESHOT("frameserver", "signal", TRACE_SYS_DEFAULT, tgt->vid, 0, "");
685 			platform_fsrv_pushevent(tgt, &(struct arcan_event){
686 				.category = EVENT_TARGET,
687 				.tgt.kind = TARGET_COMMAND_STEPFRAME,
688 				.tgt.ioevs[0].iv = 1,
689 				.tgt.ioevs[1].iv = 0
690 			});
691 		}
692 
693 	platform_fsrv_leave();
694 	return 0;
695 }
696 
697 enum arcan_ffunc_rv arcan_frameserver_vdirect FFUNC_HEAD
698 {
699 	int rv = FRV_NOFRAME;
700 	bool do_aud = false;
701 
702 	if (state.tag != ARCAN_TAG_FRAMESERV || !state.ptr)
703 		return rv;
704 
705 	arcan_frameserver* tgt = state.ptr;
706 	struct arcan_shmif_page* shmpage = tgt->shm.ptr;
707 	if (!shmpage)
708 		return FRV_NOFRAME;
709 
710 /* complexity note here: this is to guard against SIGBUS, which becomes
711  * quite ugly with ftruncate on a shared page that should support resize
712  * under certain conditions. */
713 	TRAMP_GUARD(FRV_NOFRAME, tgt);
714 
715 	if (tgt->segid == SEGID_UNKNOWN){
716 		arcan_frameserver_tick_control(tgt, false, FFUNC_VFRAME);
717 		goto no_out;
718 	}
719 
720 	switch (cmd){
721 /* silent compiler, this should not happen for a target with a
722  * frameserver feeding it */
723 	case FFUNC_READBACK:
724 	break;
725 
726 	case FFUNC_POLL:
727 		if (shmpage->resized){
728 			arcan_frameserver_tick_control(tgt, false, FFUNC_VFRAME);
729 			goto no_out;
730 		}
731 
732 		if (tgt->playstate != ARCAN_PLAYING)
733 			goto no_out;
734 
735 		if (tgt->n_pending)
736 			flush_queued(tgt);
737 
738 /* use this opportunity to make sure that we treat audio as well,
739  * when theres the one there is usually the other */
740 		do_aud =
741 			(atomic_load(&tgt->shm.ptr->aready) > 0 &&
742 			atomic_load(&tgt->shm.ptr->apending) > 0);
743 
744 		if (tgt->flags.autoclock && tgt->clock.frame)
745 			autoclock_frame(tgt);
746 
747 /* caller uses this hint to determine if a transfer should be
748  * initiated or not */
749 		rv = (tgt->shm.ptr->vready &&
750 			!tgt->flags.release_pending) ? FRV_GOTFRAME : FRV_NOFRAME;
751 	break;
752 
753 	case FFUNC_TICK:
754 		if (!arcan_frameserver_tick_control(tgt, true, FFUNC_VFRAME))
755 			goto no_out;
756 	break;
757 
758 	case FFUNC_DESTROY:
759 		TRACE_MARK_ONESHOT("frameserver", "free", TRACE_SYS_DEFAULT, tgt->vid, 0, "");
760 		arcan_frameserver_free( tgt );
761 	break;
762 
763 	case FFUNC_RENDER:
764 		TRACE_MARK_ENTER("frameserver", "queue-transfer", TRACE_SYS_DEFAULT, tgt->vid, 0, "");
765 			switch (arcan_event_queuetransfer(
766 				arcan_event_defaultctx(),
767 				&tgt->inqueue, tgt->queue_mask, tgt->xfer_sat, tgt))
768 			{
769 			case -2: /* fuse-fail, free */
770 				arcan_frameserver_free( tgt );
771 				TRACE_MARK_EXIT("frameserver", "queue-transfer", TRACE_SYS_DEFAULT, tgt->vid, 0, "");
772 				goto no_out;
773 			break;
774 			case -1: /* re-arm, lost */
775 				TRAMP_GUARD(FRV_NOFRAME, tgt);
776 			break;
777 			default: break;
778 			}
779 		TRACE_MARK_EXIT("frameserver", "queue-transfer", TRACE_SYS_DEFAULT, tgt->vid, 0, "");
780 
781 		struct arcan_vobject* vobj = arcan_video_getobject(tgt->vid);
782 
783 /* frameset can be set to round-robin rotate, so with a frameset we first
784  * find the related vstore and if not, the default */
785 		struct agp_vstore* dst_store = vobj->frameset ?
786 			vobj->frameset->frames[vobj->frameset->index].frame : vobj->vstore;
787 		struct arcan_shmif_region dirty = atomic_load(&shmpage->dirty);
788 
789 /* while we're here, check if audio should be processed as well */
790 		do_aud = (atomic_load(&tgt->shm.ptr->aready) > 0 &&
791 			atomic_load(&tgt->shm.ptr->apending) > 0);
792 
793 /* sometimes, the buffer transfer is forcibly deferred and this needs
794  * to be repeat until it succeeds - this mechanism could/should(?) also
795  * be used with the vpts- below, simply defer until the deadline has
796  * passed */
797 		if (g_buffers_locked == 1 || tgt->flags.locked || !push_buffer(tgt,
798 				dst_store, shmpage->hints & SHMIF_RHINT_SUBREGION ? &dirty : NULL)){
799 			goto no_out;
800 		}
801 
802 /* for tighter latency management, here is where the estimated next
803  * synch deadline for any output it is used on could/should be set,
804  * though it feeds back into the need of the conductor- refactor */
805 		dst_store->vinf.text.vpts = shmpage->vpts;
806 
807 /* for some connections, we want additional statistics */
808 		if (tgt->desc.callback_framestate)
809 			emit_deliveredframe(tgt, shmpage->vpts, tgt->desc.framecount);
810 		tgt->desc.framecount++;
811 		TRACE_MARK_ONESHOT("frameserver", "frame", TRACE_SYS_DEFAULT, tgt->vid, tgt->desc.framecount, "");
812 
813 /* interactive frameserver blocks on vsemaphore only,
814  * so set monitor flags and wake up */
815 		if (g_buffers_locked != 2){
816 			atomic_store_explicit(&shmpage->vready, 0, memory_order_release);
817 
818 			arcan_sem_post( tgt->vsync );
819 			if (tgt->desc.hints & SHMIF_RHINT_VSIGNAL_EV){
820 				TRACE_MARK_ONESHOT("frameserver", "signal", TRACE_SYS_DEFAULT, tgt->vid, 0, "");
821 				platform_fsrv_pushevent(tgt, &(struct arcan_event){
822 					.category = EVENT_TARGET,
823 					.tgt.kind = TARGET_COMMAND_STEPFRAME,
824 					.tgt.ioevs[0].iv = 1,
825 					.tgt.ioevs[1].iv = 0
826 				});
827 			}
828 		}
829 		else
830 			tgt->flags.release_pending = true;
831 	break;
832 
833 	case FFUNC_ADOPT:
834 		default_adoph(tgt, srcid);
835 	break;
836   }
837 
838 no_out:
839 	platform_fsrv_leave();
840 
841 /* we need to defer the fake invocation here to not mess with
842  * the signal- guard */
843 	if (do_aud){
844 		arcan_aid_refresh(tgt->aid);
845 	}
846 
847 	return rv;
848 }
849 
850 /*
851  * a little bit special, the vstore is already assumed to contain the state
852  * that we want to forward, and there's no audio mixing or similar going on, so
853  * just copy.
854  */
855 enum arcan_ffunc_rv arcan_frameserver_feedcopy FFUNC_HEAD
856 {
857 	assert(state.ptr);
858 	assert(state.tag == ARCAN_TAG_FRAMESERV);
859 	arcan_frameserver* src = (arcan_frameserver*) state.ptr;
860 
861 	TRAMP_GUARD(FRV_NOFRAME, src);
862 
863 	if (cmd == FFUNC_DESTROY)
864 		arcan_frameserver_free(state.ptr);
865 
866 	else if (cmd == FFUNC_ADOPT)
867 		default_adoph(src, srcid);
868 
869 	else if (cmd == FFUNC_POLL){
870 /* done differently since we don't care if the frameserver
871  * wants to resize segments used for recording */
872 		if (!arcan_frameserver_control_chld(src)){
873 			platform_fsrv_leave();
874 			return FRV_NOFRAME;
875 		}
876 
877 /* only push an update when the target is ready and the
878  * monitored source has updated its backing store */
879 		if (!src->shm.ptr->vready){
880 			arcan_vobject* me = arcan_video_getobject(src->vid);
881 			if (me->vstore->update_ts == src->desc.synch_ts)
882 				goto leave;
883 			src->desc.synch_ts = me->vstore->update_ts;
884 
885 			if (src->shm.ptr->w!=me->vstore->w || src->shm.ptr->h!=me->vstore->h){
886 				arcan_frameserver_free(state.ptr);
887 				goto leave;
888 			}
889 
890 			arcan_event ev  = {
891 				.tgt.kind = TARGET_COMMAND_STEPFRAME,
892 				.category = EVENT_TARGET,
893 				.tgt.ioevs[0] = src->vfcount++
894 			};
895 
896 			memcpy(src->vbufs[0],
897 				me->vstore->vinf.text.raw, me->vstore->vinf.text.s_raw);
898 			src->shm.ptr->vpts = me->vstore->vinf.text.vpts;
899 			src->shm.ptr->vready = true;
900 			FORCE_SYNCH();
901 
902 			if (ARCAN_OK != platform_fsrv_pushevent(src, &ev) &&
903 				src->n_pending < COUNT_OF(src->pending_queue)){
904 				src->pending_queue[src->n_pending++] = ev;
905 			}
906 		}
907 
908 		if (src->flags.autoclock && src->clock.frame)
909 			autoclock_frame(src);
910 
911 		if (-2 ==
912 			arcan_event_queuetransfer(arcan_event_defaultctx(),
913 				&src->inqueue, src->queue_mask, src->xfer_sat, src)){
914 			arcan_frameserver_free(src);
915 		}
916 	}
917 
918 leave:
919 	platform_fsrv_leave();
920 	return FRV_NOFRAME;
921 }
922 
923 enum arcan_ffunc_rv arcan_frameserver_avfeedframe FFUNC_HEAD
924 {
925 	assert(state.ptr);
926 	assert(state.tag == ARCAN_TAG_FRAMESERV);
927 	arcan_frameserver* src = (arcan_frameserver*) state.ptr;
928 
929 	TRAMP_GUARD(FRV_NOFRAME, src);
930 
931 	if (cmd == FFUNC_DESTROY)
932 		arcan_frameserver_free(state.ptr);
933 
934 	else if (cmd == FFUNC_ADOPT)
935 		default_adoph(src, srcid);
936 
937 	else if (cmd == FFUNC_TICK){
938 /* done differently since we don't care if the frameserver
939  * wants to resize segments used for recording */
940 		if (!arcan_frameserver_control_chld(src))
941 			goto no_out;
942 
943 		arcan_event_queuetransfer(
944 			arcan_event_defaultctx(), &src->inqueue, src->queue_mask, 0.5, src);
945 	}
946 /* mark that we are actually busy still */
947 	else if (cmd == FFUNC_POLL){
948 		if (atomic_load(&src->shm.ptr->vready)){
949 			return FRV_GOTFRAME;
950 		}
951 	}
952 /*
953  * if the frameserver isn't ready to receive (semaphore unlocked) then the
954  * frame will be dropped, a warning noting that the frameserver isn't fast
955  * enough to deal with the data (allowed to duplicate frame to maintain
956  * framerate, it can catch up reasonably by using less CPU intensive frame
957  * format. Audio will keep on buffering until overflow.
958  */
959 	else if (cmd == FFUNC_READBACK){
960 		if (src->shm.ptr && !src->shm.ptr->vready){
961 			memcpy(src->vbufs[0], buf, buf_sz);
962 			if (src->ofs_audb){
963 				memcpy(src->abufs[0], src->audb, src->ofs_audb);
964 				src->shm.ptr->abufused[0] = src->ofs_audb;
965 				src->ofs_audb = 0;
966 			}
967 
968 /*
969  * it is possible that we deliver more videoframes than we can legitimately
970  * encode in the target framerate, it is up to the frameserver to determine
971  * when to drop and when to double frames
972  */
973 			arcan_event ev  = {
974 				.tgt.kind = TARGET_COMMAND_STEPFRAME,
975 				.category = EVENT_TARGET,
976 				.tgt.ioevs[0] = src->vfcount++
977 			};
978 			struct arcan_shmif_region reg;
979 			if (src->desc.region_valid)
980 				reg = src->desc.region;
981 			else
982 				reg = (struct arcan_shmif_region){
983 					.x2 = src->desc.width, .y2 = src->desc.height
984 				};
985 
986 			atomic_store(&src->shm.ptr->vpts, arcan_timemillis());
987 			atomic_store(&src->shm.ptr->dirty, reg);
988 			atomic_store(&src->shm.ptr->vready, 1);
989 			platform_fsrv_pushevent(src, &ev);
990 
991 			if (src->desc.callback_framestate)
992 				emit_deliveredframe(src, 0, src->desc.framecount++);
993 		}
994 		else {
995 			if (src->desc.callback_framestate)
996 				emit_droppedframe(src, 0, src->desc.dropcount++);
997 		}
998 	}
999 	else
1000 			;
1001 
1002 no_out:
1003 	platform_fsrv_leave();
1004 	return FRV_NOFRAME;
1005 }
1006 
1007 /* assumptions:
1008  * buf_sz doesn't contain partial samples (% (bytes per sample * channels))
1009  * dst->amixer inaud is allocated and allocation count matches n_aids */
feed_amixer(arcan_frameserver * dst,arcan_aobj_id srcid,int16_t * buf,int nsamples)1010 static void feed_amixer(arcan_frameserver* dst, arcan_aobj_id srcid,
1011 	int16_t* buf, int nsamples)
1012 {
1013 /* formats; nsamples (samples in, 2 samples / frame)
1014  * cur->inbuf; samples converted to float with gain, 2 samples / frame)
1015  * dst->outbuf; SINT16, in bytes, ofset in bytes */
1016 	size_t minv = INT_MAX;
1017 
1018 /* 1. Convert to float and buffer. Find the lowest common number of samples
1019  * buffered. Truncate if needed. Assume source feeds L/R */
1020 	for (int i = 0; i < dst->amixer.n_aids; i++){
1021 		struct frameserver_audsrc* cur = dst->amixer.inaud + i;
1022 
1023 		if (cur->src_aid == srcid){
1024 			int ulim = sizeof(cur->inbuf) / sizeof(float);
1025 			int count = 0;
1026 
1027 			while (nsamples-- && cur->inofs < ulim){
1028 				float val = *buf++;
1029 				cur->inbuf[cur->inofs++] =
1030 					(count++ % 2 ? cur->l_gain : cur->r_gain) * (val / 32767.0f);
1031 			}
1032 		}
1033 
1034 		if (cur->inofs < minv)
1035 			minv = cur->inofs;
1036 	}
1037 
1038 /*
1039  * 2. If number of samples exceeds some threshold, mix (minv)
1040  * samples together and store in dst->outb Formulae used:
1041  * A = float(sampleA) * gainA.
1042  * B = float(sampleB) * gainB. Z = A + B - A * B
1043  */
1044 		if (minv != INT_MAX && minv > 512 && dst->sz_audb - dst->ofs_audb > 0){
1045 /* clamp */
1046 			if (dst->ofs_audb + minv * sizeof(uint16_t) > dst->sz_audb)
1047 				minv = (dst->sz_audb - dst->ofs_audb) / sizeof(uint16_t);
1048 
1049 			for (int sc = 0; sc < minv; sc++){
1050 				float work_sample = 0;
1051 
1052 			for (int i = 0; i < dst->amixer.n_aids; i++){
1053 				work_sample += dst->amixer.inaud[i].inbuf[sc] - (work_sample *
1054 					dst->amixer.inaud[i].inbuf[sc]);
1055 			}
1056 /* clip output */
1057 			int16_t sample_conv = work_sample >= 1.0 ? 32767.0 :
1058 				(work_sample < -1.0 ? -32768 : work_sample * 32767);
1059 			memcpy(&dst->audb[dst->ofs_audb], &sample_conv, sizeof(int16_t));
1060 			dst->ofs_audb += sizeof(int16_t);
1061 		}
1062 /* 2b. Reset intermediate buffers, slide if needed. */
1063 		for (int j = 0; j < dst->amixer.n_aids; j++){
1064 			struct frameserver_audsrc* cur = dst->amixer.inaud + j;
1065 			if (cur->inofs > minv){
1066 				memmove(cur->inbuf, &cur->inbuf[minv], (cur->inofs - minv) *
1067 					sizeof(float));
1068 				cur->inofs -= minv;
1069 			}
1070 			else
1071 				cur->inofs = 0;
1072 		}
1073 	}
1074 
1075 }
1076 
arcan_frameserver_update_mixweight(arcan_frameserver * dst,arcan_aobj_id src,float left,float right)1077 void arcan_frameserver_update_mixweight(arcan_frameserver* dst,
1078 	arcan_aobj_id src, float left, float right)
1079 {
1080 	for (int i = 0; i < dst->amixer.n_aids; i++){
1081 		if (src == 0 || dst->amixer.inaud[i].src_aid == src){
1082 			dst->amixer.inaud[i].l_gain = left;
1083 			dst->amixer.inaud[i].r_gain = right;
1084 		}
1085 	}
1086 }
1087 
arcan_frameserver_avfeed_mixer(arcan_frameserver * dst,int n_sources,arcan_aobj_id * sources)1088 void arcan_frameserver_avfeed_mixer(arcan_frameserver* dst, int n_sources,
1089 	arcan_aobj_id* sources)
1090 {
1091 	assert(sources != NULL && dst != NULL && n_sources > 0);
1092 
1093 	if (dst->amixer.n_aids)
1094 		arcan_mem_free(dst->amixer.inaud);
1095 
1096 	dst->amixer.inaud = arcan_alloc_mem(
1097 		n_sources * sizeof(struct frameserver_audsrc),
1098 		ARCAN_MEM_ATAG, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_NATURAL);
1099 
1100 	for (int i = 0; i < n_sources; i++){
1101 		dst->amixer.inaud[i].l_gain  = 1.0;
1102 		dst->amixer.inaud[i].r_gain  = 1.0;
1103 		dst->amixer.inaud[i].inofs   = 0;
1104 		dst->amixer.inaud[i].src_aid = *sources++;
1105 	}
1106 
1107 	dst->amixer.n_aids = n_sources;
1108 }
1109 
arcan_frameserver_avfeedmon(arcan_aobj_id src,uint8_t * buf,size_t buf_sz,unsigned channels,unsigned frequency,void * tag)1110 void arcan_frameserver_avfeedmon(arcan_aobj_id src, uint8_t* buf,
1111 	size_t buf_sz, unsigned channels, unsigned frequency, void* tag)
1112 {
1113 	arcan_frameserver* dst = tag;
1114 	assert((intptr_t)(buf) % 4 == 0);
1115 
1116 /*
1117  * FIXME: This is a victim of a recent shmif refactor where we started to
1118  * allow negotating different samplerates. This should be complemented with
1119  * the speex- resampler that's in the codebase already
1120  */
1121 	if (frequency != ARCAN_SHMIF_SAMPLERATE){
1122 		static bool warn;
1123 		if (!warn){
1124 			arcan_warning("arcan_frameserver_avfeedmon(), monitoring an audio feed\n"
1125 				"with a non-native samplerate, this is >currently< supported for\n"
1126 				"playback but not for recording (TOFIX).\n");
1127 			warn = true;
1128 		}
1129 	}
1130 
1131 /*
1132  * with no mixing setup (lowest latency path), we just feed the sync buffer
1133  * shared with the frameserver. otherwise we forward to the amixer that is
1134  * responsible for pushing as much as has been generated by all the defined
1135  * sources
1136  */
1137 	if (dst->amixer.n_aids > 0){
1138 		feed_amixer(dst, src, (int16_t*) buf, buf_sz >> 1);
1139 	}
1140 	else if (dst->ofs_audb + buf_sz < dst->sz_audb){
1141 			memcpy(dst->audb + dst->ofs_audb, buf, buf_sz);
1142 			dst->ofs_audb += buf_sz;
1143 	}
1144 	else;
1145 }
1146 
emit_deliveredframe(arcan_frameserver * src,unsigned long long pts,unsigned long long framecount)1147 static inline void emit_deliveredframe(arcan_frameserver* src,
1148 	unsigned long long pts, unsigned long long framecount)
1149 {
1150 	arcan_event ev = {
1151 		.category = EVENT_FSRV,
1152 		.fsrv.kind = EVENT_FSRV_DELIVEREDFRAME,
1153 		.fsrv.pts = pts,
1154 		.fsrv.counter = framecount,
1155 		.fsrv.otag = src->tag,
1156 		.fsrv.audio = src->aid,
1157 		.fsrv.video = src->vid
1158 	};
1159 
1160 	if (src->desc.region_valid){
1161 		ev.fsrv.xofs = src->desc.region.x1;
1162 		ev.fsrv.yofs = src->desc.region.y1;
1163 		ev.fsrv.width = src->desc.region.x2 - src->desc.region.x1;
1164 		ev.fsrv.height = src->desc.region.y2 - src->desc.region.y1;
1165 	}
1166 	else{
1167 		ev.fsrv.width = src->desc.width;
1168 		ev.fsrv.height = src->desc.height;
1169 	}
1170 
1171 	arcan_event_enqueue(arcan_event_defaultctx(), &ev);
1172 }
1173 
emit_droppedframe(arcan_frameserver * src,unsigned long long pts,unsigned long long dropcount)1174 static inline void emit_droppedframe(arcan_frameserver* src,
1175 	unsigned long long pts, unsigned long long dropcount)
1176 {
1177 	arcan_event deliv = {
1178 		.category = EVENT_FSRV,
1179 		.fsrv.kind = EVENT_FSRV_DROPPEDFRAME,
1180 		.fsrv.pts = pts,
1181 		.fsrv.counter = dropcount,
1182 		.fsrv.otag = src->tag,
1183 		.fsrv.audio = src->aid,
1184 		.fsrv.video = src->vid
1185 	};
1186 
1187 	arcan_event_enqueue(arcan_event_defaultctx(), &deliv);
1188 }
1189 
arcan_frameserver_getramps(arcan_frameserver * src,size_t index,float * table,size_t table_sz,size_t * ch_sz)1190 bool arcan_frameserver_getramps(arcan_frameserver* src,
1191 	size_t index, float* table, size_t table_sz, size_t* ch_sz)
1192 {
1193 	if (!ch_sz || !table || !src || !src->desc.aext.gamma)
1194 		return false;
1195 
1196 	struct arcan_shmif_ramp* hdr = src->desc.aext.gamma;
1197 	if (!hdr || hdr->magic != ARCAN_SHMIF_RAMPMAGIC)
1198 		return false;
1199 
1200 /* note, we can't rely on the page block counter as the source isn't trusted to
1201  * set/manage that information, rely on the requirement that display- index set
1202  * size is constant */
1203 	size_t lim;
1204 	platform_video_displays(NULL, &lim);
1205 
1206 	if (index >= lim)
1207 		return false;
1208 
1209 /* only allow ramp-retrieval once and only for one that is marked dirty */
1210 	if (!(hdr->dirty_out & (1 << index)))
1211 		return false;
1212 
1213 /* we always ignore EDID, that one is read/only */
1214 	struct ramp_block block;
1215 	memcpy(&block, &hdr->ramps[index * 2], sizeof(struct ramp_block));
1216 
1217 	uint16_t checksum = subp_checksum(
1218 		block.edid, sizeof(block.edid) + SHMIF_CMRAMP_UPLIM);
1219 
1220 /* Checksum verification failed, either this has been done deliberately and
1221  * we're in a bit of a situation since there's no strong mechanism for the
1222  * caller to know this is the reason and that we need to wait and recheck.
1223  * If we revert the tracking-bit, the event updates will be correct again,
1224  * but we are introducing a 'sortof' event-queue storm of 1 event/tick.
1225  *
1226  * The decision, for now, is to take that risk and let the ratelimit+kill
1227  * action happen in the scripting layer. */
1228 
1229 	src->desc.aext.gamma_map &= ~(1 << index);
1230 	if (checksum != block.checksum)
1231 		return false;
1232 
1233 	memcpy(table, block.planes,
1234 		table_sz < SHMIF_CMRAMP_UPLIM ? table_sz : SHMIF_CMRAMP_UPLIM);
1235 
1236 	memcpy(ch_sz, block.plane_sizes, sizeof(size_t)*SHMIF_CMRAMP_PLIM);
1237 
1238 	atomic_fetch_and(&hdr->dirty_out, ~(1<<index));
1239 	return true;
1240 }
1241 
1242 /*
1243  * Used to update a specific client's perception of a specific ramp,
1244  * the index must be < (ramp_limit-1) which is a platform defined static
1245  * upper limit of supported number of displays and ramps.
1246  */
arcan_frameserver_setramps(arcan_frameserver * src,size_t index,float * table,size_t table_sz,size_t ch_sz[SHMIF_CMRAMP_PLIM],uint8_t * edid,size_t edid_sz)1247 bool arcan_frameserver_setramps(arcan_frameserver* src,
1248 	size_t index,
1249 	float* table, size_t table_sz,
1250 	size_t ch_sz[SHMIF_CMRAMP_PLIM],
1251 	uint8_t* edid, size_t edid_sz)
1252 {
1253 	if (!ch_sz || !table || !src || !src->desc.aext.gamma)
1254 		return false;
1255 
1256 	size_t lim;
1257 	platform_video_displays(NULL, &lim);
1258 
1259 	if (index >= lim)
1260 		return false;
1261 
1262 /* verify that we fit */
1263 	size_t sum = 0;
1264 	for (size_t i = 0; i < SHMIF_CMRAMP_PLIM; i++){
1265 		sum += ch_sz[i];
1266 	}
1267 	if (sum > SHMIF_CMRAMP_UPLIM)
1268 		return false;
1269 
1270 /* prepare a local copy and throw it in there */
1271 	struct ramp_block block = {0};
1272 	memcpy(block.plane_sizes, ch_sz, sizeof(size_t)*SHMIF_CMRAMP_PLIM);
1273 
1274 /* first the actual samples */
1275 	size_t pdata_sz = SHMIF_CMRAMP_UPLIM;
1276 	if (pdata_sz > table_sz)
1277 		pdata_sz = table_sz;
1278 	memcpy(block.planes, table, pdata_sz);
1279 
1280 /* EDID block is optional, full == 0 to indicate disabled */
1281 	size_t edid_bsz = sizeof(src->desc.aext.gamma->ramps[index].edid);
1282 	if (edid && edid_sz == edid_bsz)
1283 		memcpy(src->desc.aext.gamma->ramps[index].edid, edid, edid_bsz);
1284 
1285 /* checksum only applies to edid+planedata */
1286 	block.checksum = subp_checksum(
1287 		(uint8_t*)block.edid, edid_bsz + SHMIF_CMRAMP_UPLIM);
1288 
1289 /* flush- to buffer */
1290 	memcpy(&src->desc.aext.gamma->ramps[index], &block, sizeof(block));
1291 
1292 /* and set the magic bit */
1293 	atomic_fetch_or(&src->desc.aext.gamma->dirty_in, 1 << index);
1294 
1295 	return true;
1296 }
1297 
1298 /*
1299  * This is a legacy- feed interface and doesn't reflect how the shmif audio
1300  * buffering works. Hence we ignore queing to the selected buffer, and instead
1301  * use a populate function to retrieve at most n' buffers that we then fill.
1302  */
arcan_frameserver_audioframe_direct(arcan_aobj * aobj,arcan_aobj_id id,unsigned buffer,bool cont,void * tag)1303 arcan_errc arcan_frameserver_audioframe_direct(arcan_aobj* aobj,
1304 	arcan_aobj_id id, unsigned buffer, bool cont, void* tag)
1305 {
1306 	arcan_frameserver* src = (arcan_frameserver*) tag;
1307 
1308 	if (buffer == -1 || src->segid == SEGID_UNKNOWN)
1309 		return ARCAN_ERRC_NOTREADY;
1310 
1311 	assert(src->watch_const == 0xfeed);
1312 
1313 /* we need to switch to an interface where we can retrieve a set number of
1314  * buffers, matching the number of set bits in amask, then walk from ind-1 and
1315  * buffering all */
1316 	if (!src->shm.ptr)
1317 		return ARCAN_ERRC_UNACCEPTED_STATE;
1318 
1319 	TRAMP_GUARD(ARCAN_ERRC_UNACCEPTED_STATE, src);
1320 
1321 	volatile int ind = atomic_load(&src->shm.ptr->aready) - 1;
1322 	volatile int amask = atomic_load(&src->shm.ptr->apending);
1323 
1324 /* sanity check, untrusted source */
1325 	if (ind >= src->abuf_cnt || ind < 0){
1326 		platform_fsrv_leave(src);
1327 		return ARCAN_ERRC_NOTREADY;
1328 	}
1329 
1330 	if (0 == amask || ((1<<ind)&amask) == 0){
1331 		atomic_store_explicit(&src->shm.ptr->aready, 0, memory_order_release);
1332 		platform_fsrv_leave(src);
1333 		arcan_sem_post(src->async);
1334 		return ARCAN_ERRC_NOTREADY;
1335 	}
1336 
1337 /* find oldest buffer, we know there is at least one */
1338 	int i = ind, prev;
1339 	do {
1340 		prev = i;
1341 		i--;
1342 		if (i < 0)
1343 			i = src->abuf_cnt-1;
1344 	} while (i != ind && ((1<<i)&amask) > 0);
1345 
1346 	arcan_audio_buffer(aobj, buffer,
1347 		src->abufs[prev], src->shm.ptr->abufused[prev],
1348 		src->desc.channels, src->desc.samplerate, tag
1349 	);
1350 
1351 	atomic_store(&src->shm.ptr->abufused[prev], 0);
1352 	int last = atomic_fetch_and_explicit(&src->shm.ptr->apending,
1353 		~(1 << prev), memory_order_release);
1354 
1355 /* check for cont and > 1, wait for signal.. else release */
1356 	if (!cont){
1357 		atomic_store_explicit(&src->shm.ptr->aready, 0, memory_order_release);
1358 		platform_fsrv_leave(src);
1359 		arcan_sem_post(src->async);
1360 	}
1361 
1362 	return ARCAN_OK;
1363 }
1364 
arcan_frameserver_tick_control(arcan_frameserver * src,bool tick,int dst_ffunc)1365 bool arcan_frameserver_tick_control(
1366 	arcan_frameserver* src, bool tick, int dst_ffunc)
1367 {
1368 	bool fail = true;
1369 	if (!arcan_frameserver_control_chld(src) || src->playstate == ARCAN_PAUSED)
1370 		goto leave;
1371 
1372 /* Same event-queue transfer issues as marked elsewhere, if xfer-sat goes to
1373  * drain, VM accepts drain and modifies fsrv state special action is needed.
1374  * Nesting longjmp handlers </3 */
1375 	int rv =
1376 		arcan_event_queuetransfer(arcan_event_defaultctx(),
1377 			&src->inqueue, src->queue_mask, src->xfer_sat, src);
1378 
1379 	if (-2 == rv){
1380 		arcan_frameserver_free(src);
1381 		goto leave;
1382 	}
1383 
1384 	if (-1 == rv)
1385 		goto leave;
1386 
1387 	if (!src->shm.ptr->resized){
1388 		fail = false;
1389 		goto leave;
1390 	}
1391 
1392 	FORCE_SYNCH();
1393 
1394 /*
1395  * This used to be considered suspicious behavior but with the option to switch
1396  * buffering strategies, and the protocol for that is the same as resize, we no
1397  * longer warn, but keep the code here commented as a note to it
1398 
1399  if (src->desc.width == neww && src->desc.height == newh){
1400 		arcan_warning("tick_control(), source requested "
1401 			"resize to current dimensions.\n");
1402 		goto leave;
1403 	}
1404 
1405 	in the same fashion, we killed on resize failure, but that did not work well
1406 	with switching buffer strategies (valid buffer in one size, failed because
1407 	size over reach with other strategy, so now there's a failure mechanism.
1408  */
1409 
1410 /* Invalidate any ongoing buffer-streams */
1411 	if (src->desc.width != src->shm.ptr->w || src->desc.height != src->shm.ptr->h){
1412 		arcan_frameserver_close_bufferqueues(src, true, true);
1413 	}
1414 
1415 	int rzc = platform_fsrv_resynch(src);
1416 	if (rzc <= 0)
1417 		goto leave;
1418 	else if (rzc == 2){
1419 		arcan_event_enqueue(arcan_event_defaultctx(),
1420 			&(struct arcan_event){
1421 				.category = EVENT_FSRV,
1422 				.fsrv.kind = EVENT_FSRV_APROTO,
1423 				.fsrv.video = src->vid,
1424 				.fsrv.aproto = src->desc.aproto,
1425 				.fsrv.otag = src->tag,
1426 			});
1427 	}
1428 	fail = false;
1429 
1430 /*
1431  * at this stage, frameserver impl. should have remapped event queues,
1432  * vbuf/abufs, and signaled the connected process. Make sure we are running the
1433  * right feed function (may have been turned into another or started in a
1434  * passive one
1435  */
1436 	vfunc_state cstate = *arcan_video_feedstate(src->vid);
1437 	arcan_video_alterfeed(src->vid, dst_ffunc, cstate);
1438 
1439 /*
1440  * Check if the dirty- mask for the ramp- subproto has changed, enqueue the
1441  * ones that havn't been retrieved (hence the local map) as ramp-update events.
1442  * There's no copy- and mark as read, that has to be done from the next layer.
1443  */
1444 leave:
1445 	if (!fail && src->desc.aproto & SHMIF_META_CM && src->desc.aext.gamma){
1446 		uint8_t in_map = atomic_load(&src->desc.aext.gamma->dirty_out);
1447 		for (size_t i = 0; i < 8; i++){
1448 			if ((in_map & (1 << i)) && !(src->desc.aext.gamma_map & (1 << i))){
1449 				arcan_event_enqueue(arcan_event_defaultctx(), &(arcan_event){
1450 					.category = EVENT_FSRV,
1451 					.fsrv.kind = EVENT_FSRV_GAMMARAMP,
1452 					.fsrv.counter = i,
1453 					.fsrv.video = src->vid
1454 				});
1455 				src->desc.aext.gamma_map |= 1 << i;
1456 			}
1457 		}
1458 	}
1459 
1460 /* want the event to be queued after resize so the possible reaction (i.e.
1461  * redraw + synch) aligns with pending resize */
1462 	if (!fail && tick){
1463 		if (0 >= --src->clock.left){
1464 			src->clock.left = src->clock.start;
1465 			platform_fsrv_pushevent(src, &(struct arcan_event){
1466 				.category = EVENT_TARGET,
1467 				.tgt.kind = TARGET_COMMAND_STEPFRAME,
1468 				.tgt.ioevs[0].iv = 1,
1469 				.tgt.ioevs[1].iv = 1
1470 			});
1471 		}
1472 	}
1473 	return !fail;
1474 }
1475 
arcan_frameserver_pause(arcan_frameserver * src)1476 arcan_errc arcan_frameserver_pause(arcan_frameserver* src)
1477 {
1478 	arcan_errc rv = ARCAN_ERRC_NO_SUCH_OBJECT;
1479 
1480 	if (src) {
1481 		src->playstate = ARCAN_PAUSED;
1482 		rv = ARCAN_OK;
1483 	}
1484 
1485 	return rv;
1486 }
1487 
arcan_frameserver_resume(arcan_frameserver * src)1488 arcan_errc arcan_frameserver_resume(arcan_frameserver* src)
1489 {
1490 	arcan_errc rv = ARCAN_ERRC_NO_SUCH_OBJECT;
1491 	if (src)
1492 		src->playstate = ARCAN_PLAYING;
1493 
1494 	return rv;
1495 }
1496 
arcan_frameserver_flush(arcan_frameserver * fsrv)1497 arcan_errc arcan_frameserver_flush(arcan_frameserver* fsrv)
1498 {
1499 	if (!fsrv)
1500 		return ARCAN_ERRC_NO_SUCH_OBJECT;
1501 
1502 	arcan_audio_rebuild(fsrv->aid);
1503 
1504 	return ARCAN_OK;
1505 }
1506 
arcan_frameserver_setfont(struct arcan_frameserver * fsrv,int fd,float sz,int hint,int slot)1507 arcan_errc arcan_frameserver_setfont(
1508 	struct arcan_frameserver* fsrv, int fd, float sz, int hint, int slot)
1509 {
1510 	if (!fsrv)
1511 		return ARCAN_ERRC_NO_SUCH_OBJECT;
1512 
1513 	bool replace = true;
1514 	bool reprobe = false;
1515 
1516 /* always update primary slot size */
1517 	if (slot == 0){
1518 		if (sz > EPSILON){
1519 			fsrv->desc.text.szmm = sz;
1520 			reprobe = true;
1521 		}
1522 
1523 /* first time and main slot? then build the group */
1524 		if (!fsrv->desc.text.group){
1525 			if (fsrv->desc.hint.ppcm < EPSILON){
1526 				arcan_vobject* vobj = arcan_video_getobject(fsrv->vid);
1527 
1528 /* Protect against someone in the future creating a frameserver first and
1529  * force-setting a font in the platform or elsewhere without properly attaching
1530  * it to a valid and attached vobject - note that vppcm/hppcm are treated as
1531  * uniform here which isn't entirely correct. Freetype can deal with vdpi!=hdpi
1532  * but we missed it in the DISPLAYHINT event format "square assumed" */
1533 				struct rendertarget* tgt;
1534 				if (!vobj || !(tgt = arcan_vint_findrt(vobj))){
1535 					fsrv->desc.hint.ppcm = 38.7;
1536 				}
1537 				else {
1538 					fsrv->desc.hint.ppcm = tgt->vppcm;
1539 				}
1540 			}
1541 
1542 			fsrv->desc.text.group =
1543 				arcan_renderfun_fontgroup((int[]){dup(fd), BADFD, BADFD, BADFD}, 4);
1544 			replace = false;
1545 		}
1546 	}
1547 
1548 /* supplementary slot but no group? */
1549 	if (!fsrv->desc.text.group){
1550 		close(fd);
1551 		return ARCAN_ERRC_UNACCEPTED_STATE;
1552 	}
1553 
1554 	if (replace && fd != -1)
1555 		arcan_renderfun_fontgroup_replace(fsrv->desc.text.group, slot, fd);
1556 
1557 	if (reprobe && fsrv->desc.hint.ppcm > EPSILON){
1558 		arcan_renderfun_fontgroup_size(fsrv->desc.text.group,
1559 			fsrv->desc.text.szmm, fsrv->desc.hint.ppcm,
1560 			&fsrv->desc.text.cellw, &fsrv->desc.text.cellh);
1561 	}
1562 
1563 	return ARCAN_OK;
1564 }
1565 
arcan_frameserver_displayhint(struct arcan_frameserver * fsrv,size_t w,size_t h,float ppcm)1566 void arcan_frameserver_displayhint(
1567 	struct arcan_frameserver* fsrv, size_t w, size_t h, float ppcm)
1568 {
1569 	if (!fsrv)
1570 		return;
1571 
1572 	if (w > 0)
1573 		fsrv->desc.hint.width = w;
1574 
1575 	if (h > 0)
1576 		fsrv->desc.hint.height = h;
1577 
1578 /* if we have a new density, this should be forwarded to any attached
1579  * rasterizer which may cause a different cell size to be communicated */
1580 	if (ppcm > EPSILON && ppcm != fsrv->desc.hint.ppcm){
1581 		fsrv->desc.hint.ppcm = ppcm;
1582 
1583 /* we don't actually care to use the raster, just want to re-probe size */
1584 		if (fsrv->desc.text.group){
1585 			arcan_renderfun_fontgroup_size(fsrv->desc.text.group,
1586 				0, ppcm, &fsrv->desc.text.cellw, &fsrv->desc.text.cellh);
1587 		}
1588 	}
1589 }
1590