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