From 082ff4eb7b58a49d8270752308f38159ce80f18f Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Thu, 17 Mar 2022 16:40:37 +0900 Subject: [PATCH] Add an example of using ds_tdm_buffer_queue Dependeing on the declaration of USE_TDM_BUFFER_QUEUE macro, tinyds-tdm may be run with ds_tbm_buffer_queue. Change-Id: I1d221d2d5eb94024902eea66d08f5973f1914771 --- src/examples/meson.build | 3 + src/examples/pixman-helper.c | 5 +- src/examples/pixman-helper.h | 3 + src/examples/pixman-tbm-helper.c | 64 +++++++++++ src/examples/pixman-tbm-helper.h | 13 +++ src/examples/tbm-server-helper.c | 24 ++-- src/examples/tbm-server-helper.h | 6 + src/examples/tinyds-tdm-renderer.c | 187 ++++++++++++++++++++++++++++++ src/examples/tinyds-tdm-renderer.h | 48 ++++++++ src/examples/tinyds-tdm.c | 227 +++++++++++++++++++++++++++++-------- 10 files changed, 520 insertions(+), 60 deletions(-) create mode 100644 src/examples/pixman-tbm-helper.c create mode 100644 src/examples/pixman-tbm-helper.h create mode 100644 src/examples/tinyds-tdm-renderer.c create mode 100644 src/examples/tinyds-tdm-renderer.h diff --git a/src/examples/meson.build b/src/examples/meson.build index f95bd50..c60b610 100644 --- a/src/examples/meson.build +++ b/src/examples/meson.build @@ -35,8 +35,10 @@ if get_option('tizen') tinyds_tdm_files = [ 'tinyds-tdm.c', + 'tinyds-tdm-renderer.c', 'tbm-server-helper.c', 'pixman-helper.c', + 'pixman-tbm-helper.c', ] executable('tinyds-tdm', tinyds_tdm_files, @@ -46,6 +48,7 @@ if get_option('tizen') dependency('libdrm', required: true), dependency('libtbm', required: true), dependency('wayland-tbm-server', required: true), + dependency('threads', required: true), ], install_dir: libds_bindir, install : true diff --git a/src/examples/pixman-helper.c b/src/examples/pixman-helper.c index 5d42ec6..36a22e8 100644 --- a/src/examples/pixman-helper.c +++ b/src/examples/pixman-helper.c @@ -5,8 +5,6 @@ static void destroy_pixman_image(pixman_image_t *image, void *data); static uint32_t convert_drm_format_to_pixman(uint32_t fmt); -static pixman_color_t *color_rgb888(pixman_color_t *tmp, - uint8_t r, uint8_t g, uint8_t b); pixman_image_t * pixman_image_from_buffer(struct ds_buffer *buffer, @@ -37,7 +35,6 @@ pixman_image_from_buffer(struct ds_buffer *buffer, return image; } - void pixman_image_fill_color(pixman_image_t *image, uint8_t r, uint8_t g, uint8_t b) { @@ -56,7 +53,7 @@ pixman_image_fill_color(pixman_image_t *image, uint8_t r, uint8_t g, uint8_t b) pixman_image_unref(color_image); } -static pixman_color_t * +pixman_color_t * color_rgb888(pixman_color_t *tmp, uint8_t r, uint8_t g, uint8_t b) { tmp->alpha = 65535; diff --git a/src/examples/pixman-helper.h b/src/examples/pixman-helper.h index aa039ff..99ceaca 100644 --- a/src/examples/pixman-helper.h +++ b/src/examples/pixman-helper.h @@ -12,4 +12,7 @@ void pixman_image_fill_color(pixman_image_t *image, uint8_t r, uint8_t g, uint8_t b); +pixman_color_t * +color_rgb888(pixman_color_t *tmp, uint8_t r, uint8_t g, uint8_t b); + #endif diff --git a/src/examples/pixman-tbm-helper.c b/src/examples/pixman-tbm-helper.c new file mode 100644 index 0000000..710eceb --- /dev/null +++ b/src/examples/pixman-tbm-helper.c @@ -0,0 +1,64 @@ +#include + +#include "pixman-tbm-helper.h" + +static uint32_t convert_tbm_format_to_pixman(uint32_t fmt); +static void destroy_tbm_pixman_image(pixman_image_t *image, void *data); + +pixman_image_t * +pixman_image_from_tbm_surface(tbm_surface_h surface, + enum ds_buffer_data_ptr_access_flag access_flag) +{ + pixman_image_t *image; + tbm_surface_info_s info; + uint32_t format; + int tbm_access_flag = 0; + int width, height; + int ret; + + width = tbm_surface_get_width(surface); + height = tbm_surface_get_height(surface); + + if (access_flag & DS_BUFFER_DATA_PTR_ACCESS_READ) + tbm_access_flag |= TBM_OPTION_READ; + if (access_flag & DS_BUFFER_DATA_PTR_ACCESS_WRITE) + tbm_access_flag |= TBM_OPTION_WRITE; + + ret = tbm_surface_map(surface, tbm_access_flag, &info); + assert(ret == TBM_SURFACE_ERROR_NONE); + + format = convert_tbm_format_to_pixman(info.format); + image = pixman_image_create_bits(format, width, height, + (uint32_t *)info.planes[0].ptr, + info.planes[0].stride); + assert(image); + + tbm_surface_internal_ref(surface); + + pixman_image_set_destroy_function(image, + destroy_tbm_pixman_image, surface); + + return image; +} + +static void +destroy_tbm_pixman_image(pixman_image_t *image, void *data) +{ + tbm_surface_h surface = data; + + tbm_surface_unmap(surface); + tbm_surface_internal_unref(surface); +} + +static uint32_t +convert_tbm_format_to_pixman(uint32_t fmt) +{ + switch (fmt) { + case TBM_FORMAT_XRGB8888: + return PIXMAN_x8r8g8b8; + case TBM_FORMAT_ARGB8888: + return PIXMAN_a8r8g8b8; + default: + assert(0 && "not reached"); + } +} diff --git a/src/examples/pixman-tbm-helper.h b/src/examples/pixman-tbm-helper.h new file mode 100644 index 0000000..bf9bd55 --- /dev/null +++ b/src/examples/pixman-tbm-helper.h @@ -0,0 +1,13 @@ +#ifndef EXAMPLES_PIXMAN_TBM_HELPER_H +#define EXAMPLES_PIXMAN_TBM_HELPER_H + +#include +#include +#include +#include + +pixman_image_t * +pixman_image_from_tbm_surface(tbm_surface_h surface, + enum ds_buffer_data_ptr_access_flag access_flag); + +#endif diff --git a/src/examples/tbm-server-helper.c b/src/examples/tbm-server-helper.c index bd3a34d..320ee4f 100644 --- a/src/examples/tbm-server-helper.c +++ b/src/examples/tbm-server-helper.c @@ -8,6 +8,8 @@ #include static const struct ds_buffer_resource_interface tbm_buffer_resource_iface; +static const struct ds_buffer_interface tbm_client_buffer_iface; + static void tbm_server_handle_display_destroy(struct wl_listener *listener, void *data); static struct tbm_client_buffer * @@ -42,6 +44,19 @@ tbm_server_init_display(struct tbm_server *tbm, struct wl_display *display) return true; } +struct tbm_client_buffer * +tbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer) +{ + assert(ds_buffer->iface == &tbm_client_buffer_iface); + return (struct tbm_client_buffer *)ds_buffer; +} + +tbm_surface_h +tbm_client_buffer_get_tbm_surface(struct tbm_client_buffer *buffer) +{ + return buffer->surface; +} + static void tbm_server_handle_display_destroy(struct wl_listener *listener, void *data) { @@ -107,15 +122,6 @@ static const struct ds_buffer_resource_interface tbm_buffer_resource_iface = { .from_resource = tbm_buffer_resource_iface_from_resource, }; -static const struct ds_buffer_interface tbm_client_buffer_iface; - -static struct tbm_client_buffer * -tbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer) -{ - assert(ds_buffer->iface == &tbm_client_buffer_iface); - return (struct tbm_client_buffer *)ds_buffer; -} - static void tbm_client_buffer_destroy(struct ds_buffer *ds_buffer) { diff --git a/src/examples/tbm-server-helper.h b/src/examples/tbm-server-helper.h index a82ad5b..609f370 100644 --- a/src/examples/tbm-server-helper.h +++ b/src/examples/tbm-server-helper.h @@ -32,4 +32,10 @@ bool tbm_server_init_display(struct tbm_server *tbm_server, struct wl_display *display); +struct tbm_client_buffer * +tbm_client_buffer_from_buffer(struct ds_buffer *ds_buffer); + +tbm_surface_h +tbm_client_buffer_get_tbm_surface(struct tbm_client_buffer *buffer); + #endif diff --git a/src/examples/tinyds-tdm-renderer.c b/src/examples/tinyds-tdm-renderer.c new file mode 100644 index 0000000..0074dc9 --- /dev/null +++ b/src/examples/tinyds-tdm-renderer.c @@ -0,0 +1,187 @@ +#include +#include + +#include + +#include "pixman-helper.h" +#include "pixman-tbm-helper.h" +#include "tinyds-tdm-renderer.h" + +static void renderer_setup_thread(struct tinyds_renderer *renderer); +static void *renderer_thread_func(void *data); +static void texture_destroy(struct tinyds_texture *texture); + +bool +init_renderer(struct tinyds_renderer *renderer) +{ + renderer->damaged = false; + + wl_list_init(&renderer->textures); + + renderer_setup_thread(renderer); + + return true; +} + +void +fini_renderer(struct tinyds_renderer *renderer) +{ + pthread_mutex_lock(&renderer->mutex); + + renderer->destroying = true; + pthread_cond_signal(&renderer->cond); + + pthread_mutex_unlock(&renderer->mutex); + + pthread_join(renderer->worker_thread, NULL); + + pthread_mutex_destroy(&renderer->mutex); + pthread_cond_destroy(&renderer->cond); +} + +void +renderer_set_surface_queue(struct tinyds_renderer *renderer, + void *surface_queue) +{ + pthread_mutex_lock(&renderer->mutex); + + renderer->surface_queue = (tbm_surface_queue_h)surface_queue; + + pthread_mutex_unlock(&renderer->mutex); +} + +void +renderer_set_bg_color(struct tinyds_renderer *renderer, + uint8_t r, uint8_t g, uint8_t b) +{ + pixman_color_t color; + + pthread_mutex_lock(&renderer->mutex); + + color_rgb888(&color, r, g, b); + + renderer->bg_image = pixman_image_create_solid_fill(&color); + assert(renderer->bg_image); + + renderer->damaged = true; + + pthread_mutex_unlock(&renderer->mutex); +} + +void +renderer_add_texture(struct tinyds_renderer *renderer, + tbm_surface_h tbm_surface, int x, int y) +{ + struct tinyds_texture *texture; + + pthread_mutex_lock(&renderer->mutex); + + texture = calloc(1, sizeof *texture); + + texture->x = x; + texture->y = y; + texture->renderer = renderer; + texture->surface = tbm_surface; + texture->image = pixman_image_from_tbm_surface(tbm_surface, + DS_BUFFER_DATA_PTR_ACCESS_READ); + + wl_list_insert(renderer->textures.prev, &texture->link); + + ds_dbg("Add texture(%p)", texture); + + pthread_mutex_unlock(&renderer->mutex); +} + +void +renderer_draw(struct tinyds_renderer *renderer) +{ + pthread_mutex_lock(&renderer->mutex); + + renderer->damaged = true; + pthread_cond_signal(&renderer->cond); + + pthread_mutex_unlock(&renderer->mutex); +} + +static void +renderer_setup_thread(struct tinyds_renderer *renderer) +{ + pthread_mutex_init(&renderer->mutex, NULL); + pthread_cond_init(&renderer->cond, NULL); + pthread_create(&renderer->worker_thread, NULL, + renderer_thread_func, renderer); +} + +static void * +renderer_thread_func(void *data) +{ + struct tinyds_renderer *renderer = data; + struct tinyds_texture *texture, *texture_tmp; + pixman_image_t *dst_image; + tbm_surface_h surface; + tbm_surface_queue_error_e err; + + pthread_mutex_lock(&renderer->mutex); + + while (!renderer->destroying) { + if (!renderer->damaged) + pthread_cond_wait(&renderer->cond, &renderer->mutex); + + if (!renderer->damaged) + continue; + + if (!tbm_surface_queue_can_dequeue(renderer->surface_queue, 0)) + continue; + + ds_dbg(">> BEGIN DRAW"); + + err = tbm_surface_queue_dequeue(renderer->surface_queue, &surface); + assert(err == TBM_SURFACE_QUEUE_ERROR_NONE); + + dst_image = pixman_image_from_tbm_surface(surface, + DS_BUFFER_DATA_PTR_ACCESS_WRITE); + + if (renderer->bg_image) { + pixman_image_composite32(PIXMAN_OP_SRC, + renderer->bg_image, + NULL, + dst_image, + 0, 0, 0, 0, 0, 0, + pixman_image_get_width(dst_image), + pixman_image_get_height(dst_image)); + } + + wl_list_for_each_safe(texture, texture_tmp, &renderer->textures, link) { + ds_dbg("Draw texture(%p)", texture); + pixman_image_composite32(PIXMAN_OP_OVER, + texture->image, + NULL, + dst_image, + 0, 0, 0, 0, + texture->x, texture->y, + pixman_image_get_width(texture->image), + pixman_image_get_height(texture->image)); + texture_destroy(texture); + } + pixman_image_unref(dst_image); + + err = tbm_surface_queue_enqueue(renderer->surface_queue, surface); + assert(err == TBM_SURFACE_QUEUE_ERROR_NONE); + + renderer->damaged = false; + + ds_dbg("<< END DRAW"); + } + + pthread_mutex_unlock(&renderer->mutex); + + return NULL; +} + +static void +texture_destroy(struct tinyds_texture *texture) +{ + pixman_image_unref(texture->image); + wl_list_remove(&texture->link); + free(texture); +} diff --git a/src/examples/tinyds-tdm-renderer.h b/src/examples/tinyds-tdm-renderer.h new file mode 100644 index 0000000..5f3e6fd --- /dev/null +++ b/src/examples/tinyds-tdm-renderer.h @@ -0,0 +1,48 @@ +#ifndef EXAMPLES_TINYDS_TDM_RENDERER_H +#define EXAMPLES_TINYDS_TDM_RENDERER_H + +#include +#include +#include +#include +#include + +struct tinyds_renderer +{ + tbm_surface_queue_h surface_queue; + + struct wl_list textures; + + pthread_t worker_thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + + pixman_image_t *bg_image; + + bool damaged; + bool destroying; +}; + +struct tinyds_texture +{ + struct tinyds_renderer *renderer; + pixman_image_t *image; + tbm_surface_h surface; + + struct wl_list link; + struct wl_listener buffer_destroy; + + int x, y; +}; + +bool init_renderer(struct tinyds_renderer *renderer); +void fini_renderer(struct tinyds_renderer *renderer); +void renderer_set_surface_queue(struct tinyds_renderer *renderer, + void *surface_queue); +void renderer_set_bg_color(struct tinyds_renderer *renderer, + uint8_t r, uint8_t g, uint8_t b); +void renderer_add_texture(struct tinyds_renderer *renderer, + tbm_surface_h tbm_surface, int x, int y); +void renderer_draw(struct tinyds_renderer *renderer); + +#endif diff --git a/src/examples/tinyds-tdm.c b/src/examples/tinyds-tdm.c index 143d53e..6ec1156 100644 --- a/src/examples/tinyds-tdm.c +++ b/src/examples/tinyds-tdm.c @@ -1,6 +1,3 @@ -#include "tbm-server-helper.h" -#include "pixman-helper.h" - #include #include #include @@ -14,12 +11,23 @@ #include #include #include -#include #include #include #include #include +#define USE_TDM_BUFFER_QUEUE + +#ifdef USE_TDM_BUFFER_QUEUE +#include "pixman-tbm-helper.h" +#include "tinyds-tdm-renderer.h" +#else +#include +#endif + +#include "tbm-server-helper.h" +#include "pixman-helper.h" + #define TINYDS_UNUSED __attribute__((unused)) struct tinyds_output @@ -27,7 +35,13 @@ struct tinyds_output struct tinyds_server *server; struct ds_output *ds_output; struct ds_allocator *allocator; +#ifdef USE_TDM_BUFFER_QUEUE + struct tinyds_renderer renderer; + struct ds_tdm_buffer_queue *buffer_queue; + struct wl_listener buffer_queue_acquirable; +#else struct ds_swapchain *swapchain; +#endif struct ds_buffer *front_buffer; struct wl_listener output_destroy; @@ -62,6 +76,7 @@ struct tinyds_view { struct tinyds_server *server; + struct tinyds_texture *texture; struct ds_xdg_surface *xdg_surface; struct wl_listener xdg_surface_map; @@ -82,7 +97,18 @@ static void output_handle_destroy(struct wl_listener *listener, void *data); static void output_handle_frame(struct wl_listener *listener, void *data); static void draw_server_with_damage(struct tinyds_server *server); static void draw_output(struct tinyds_output *output); +static void output_swap_buffer(struct tinyds_output *output, + struct ds_buffer *buffer); +static void view_send_frame_done(struct tinyds_view *view); +#ifdef USE_TDM_BUFFER_QUEUE +static void output_buffer_queue_init(struct tinyds_output *output); +static void output_renderer_init(struct tinyds_output *output); +static void output_draw_with_renderer(struct tinyds_output *output); +#else +static void output_swapchain_init(struct tinyds_output *output); +static void output_draw_with_swapchain(struct tinyds_output *output); static void draw_view(struct tinyds_view *view, pixman_image_t *dst_image); +#endif int main(void) @@ -150,10 +176,10 @@ view_handle_xdg_surface_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED) { struct tinyds_view *view; + struct tinyds_server *server; view = wl_container_of(listener, view, xdg_surface_destroy); - - draw_server_with_damage(view->server); + server = view->server; wl_list_remove(&view->xdg_surface_destroy.link); wl_list_remove(&view->xdg_surface_map.link); @@ -161,6 +187,8 @@ view_handle_xdg_surface_destroy(struct wl_listener *listener, wl_list_remove(&view->surface_commit.link); wl_list_remove(&view->link); free(view); + + draw_server_with_damage(server); } static void @@ -237,32 +265,25 @@ backend_handle_new_output(struct wl_listener *listener, void *data) if (!output) return; - output->allocator = ds_tbm_allocator_create(); - if (!output->allocator) { - free(output); - return; - } - - output->swapchain = ds_swapchain_create(output->allocator, - mode->width, mode->height, DRM_FORMAT_XRGB8888); - if (!output->swapchain) { - ds_allocator_destroy(output->allocator); - free(output); - return; - } - output->server = server; output->ds_output = ds_output; + output->width = mode->width; + output->height = mode->height; output->drawable = true; output->damaged = true; +#ifdef USE_TDM_BUFFER_QUEUE + output_buffer_queue_init(output); + output_renderer_init(output); +#else + output_swapchain_init(output); +#endif + output->output_destroy.notify = output_handle_destroy; - ds_output_add_destroy_listener(ds_output, - &output->output_destroy); + ds_output_add_destroy_listener(ds_output, &output->output_destroy); output->output_frame.notify = output_handle_frame; - ds_output_add_frame_listener(ds_output, - &output->output_frame); + ds_output_add_frame_listener(ds_output, &output->output_frame); server->output = output; @@ -318,11 +339,15 @@ output_handle_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED) if (output->front_buffer) ds_buffer_unlock(output->front_buffer); +#ifdef USE_TDM_BUFFER_QUEUE + fini_renderer(&output->renderer); +#else if (output->swapchain) ds_swapchain_destroy(output->swapchain); if (output->allocator) ds_allocator_destroy(output->allocator); +#endif wl_display_terminate(output->server->display); @@ -348,15 +373,102 @@ draw_server_with_damage(struct tinyds_server *server) draw_output(server->output); } +#ifdef USE_TDM_BUFFER_QUEUE static void -draw_output(struct tinyds_output *output) +output_handle_buffer_queue_acquirable(struct wl_listener *listener, + void *data TINYDS_UNUSED) +{ + struct tinyds_output *output; + struct ds_buffer *buffer; + + output = wl_container_of(listener, output, buffer_queue_acquirable); + + buffer = ds_tdm_buffer_queue_acquire(output->buffer_queue); + assert(buffer); + + output_swap_buffer(output, buffer); +} + +static void +output_buffer_queue_init(struct tinyds_output *output) +{ + struct ds_tdm_output *tdm_output; + + tdm_output = ds_tdm_output_from_output(output->ds_output); + assert(tdm_output); + + output->buffer_queue = ds_tdm_output_get_buffer_queue(tdm_output); + assert(output->buffer_queue); + + output->buffer_queue_acquirable.notify = + output_handle_buffer_queue_acquirable; + ds_tdm_buffer_queue_add_acquirable_listener(output->buffer_queue, + &output->buffer_queue_acquirable); +} + +static void +output_renderer_init(struct tinyds_output *output) +{ + init_renderer(&output->renderer); + + renderer_set_surface_queue(&output->renderer, + ds_tdm_buffer_queue_get_native_queue(output->buffer_queue)); + + renderer_set_bg_color(&output->renderer, 80, 80, 80); +} + +static void +output_draw_with_renderer(struct tinyds_output *output) { - struct ds_buffer *output_buffer; - pixman_image_t *output_image; struct tinyds_view *view; - if (!output->drawable || !output->damaged) - return; + ds_dbg(">> BEGIN UPDATE TEXTURES"); + + wl_list_for_each(view, &output->server->views, link) { + struct ds_buffer *ds_buffer; + struct tbm_client_buffer *buffer; + tbm_surface_h surface; + + if (!view->mapped) + continue; + + ds_buffer = ds_surface_get_buffer( + ds_xdg_surface_get_surface(view->xdg_surface)); + assert(buffer); + + buffer = tbm_client_buffer_from_buffer(ds_buffer); + assert(buffer); + + surface = tbm_client_buffer_get_tbm_surface(buffer); + + renderer_add_texture(&output->renderer, surface, view->x, view->y); + + view_send_frame_done(view); + } + + ds_dbg("<< END UPDATE TEXTURES"); + + renderer_draw(&output->renderer); + +} +#else +static void +output_swapchain_init(struct tinyds_output *output) +{ + output->allocator = ds_tbm_allocator_create(); + assert(output->allocator); + + output->swapchain = ds_swapchain_create(output->allocator, + mode->width, mode->height, DRM_FORMAT_XRGB8888); + assert(output->swapchain); +} + +static void +output_draw_with_swapchain(struct tinyds_output *output) +{ + struct tinyds_view *view; + struct ds_buffer *output_buffer; + pixman_image_t *output_image; output_buffer = ds_swapchain_acquire(output->swapchain, NULL); if (!output_buffer) @@ -378,24 +490,7 @@ draw_output(struct tinyds_output *output) } pixman_image_unref(output_image); - ds_output_attach_buffer(output->ds_output, output_buffer); - ds_output_commit(output->ds_output); - - if (output->front_buffer) - ds_buffer_unlock(output->front_buffer); - output->front_buffer = output_buffer; - - output->drawable = false; - output->damaged = false; -} - -static void -view_send_frame_done(struct tinyds_view *view) -{ - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface), - &now); + output_swap_buffer(output, output_buffer); } static void @@ -423,6 +518,44 @@ draw_view(struct tinyds_view *view, pixman_image_t *dst_image) view_send_frame_done(view); } +#endif + +static void +draw_output(struct tinyds_output *output) +{ + + if (!output->drawable || !output->damaged) + return; + +#ifdef USE_TDM_BUFFER_QUEUE + output_draw_with_renderer(output); +#else + output_draw_with_swapchain(output); +#endif + + output->drawable = false; + output->damaged = false; +} + +static void +output_swap_buffer(struct tinyds_output *output, struct ds_buffer *buffer) +{ + ds_output_attach_buffer(output->ds_output, buffer); + ds_output_commit(output->ds_output); + + if (output->front_buffer) + ds_buffer_unlock(output->front_buffer); + output->front_buffer = buffer; +} + +static void +view_send_frame_done(struct tinyds_view *view) +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface), + &now); +} static int server_dispatch_stdin(int fd, uint32_t mask, void *data) -- 2.7.4