BlinkenArea - GitList
Repositories
Blog
Wiki
libetherpix
Code
Commits
Branches
Tags
Search
Tree:
693f1e8
Branches
Tags
master
libetherpix
src
config.c
move internal includes
Stefan Schuermans
commited
693f1e8
at 2017-09-24 08:51:47
config.c
Blame
History
Raw
/* * EtherPix library * * Copyright 2010-2017 Stefan Schuermans <stefan schuermans info> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <etherpix/msg.h> #include <etherpix/types.h> #include "const_data.h" #include "constants.h" #include "config.h" #include "mapping.h" #include "missing.h" #include "net.h" #include "parse.h" #include "strtod_noloc.h" #include "types.h" /** * \brief process distributor from config file * * \param[in,out] p_ctx context information * \param[in] p_setting_part2 second half of setting to process * \param[in] p_value value of setting * \return 0 in case of success, -1 in case of error */ int etp_config_proc_distri(etp_config_ctx_t *p_ctx, char *p_setting_part2, char *p_value) { char *ptr; unsigned long val; unsigned int distri, out, pix, i; etp_distri_t *p_distri; /* get distributor number */ val = strtoul(p_setting_part2, &ptr, 0); if (ptr == p_setting_part2 || *ptr != 0 || val >= ETP_DISTRI_MAX_CNT) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid distributor number \"%s\"" " in line %u of config file\n", p_setting_part2, p_ctx->line_no); return -1; } distri = (unsigned int)val; /* get number of outputs and pixels */ if (etp_parse_two_nos(p_value, &out, &pix, &ptr) || *ptr != 0) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid distributor size \"%s\"" " in line %u of config file\n", p_value, p_ctx->line_no); return -1; } if (out >= ETP_OUTPUT_MAX_CNT) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid number of outputs \"%u\"" " in line %u of config file\n", out, p_ctx->line_no); return -1; } if (pix >= ETP_PIXEL_MAX_CNT) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid number of pixels \"%u\"" " in line %u of config file\n", pix, p_ctx->line_no); return -1; } /* check if distributor is already present */ if (p_ctx->p_display->distri_ptrs[distri]) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "duplicate definition of distributor \"%u\"" " in line %u of config file\n", distri, p_ctx->line_no); return -1; } /* create a new distributor */ p_distri = (etp_distri_t *)malloc(sizeof (etp_distri_t)); if (!p_distri) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "out of memory\n"); return -1; } p_distri->distri = distri; p_distri->output_cnt = out; p_distri->pixel_cnt = pix; /* no network address yet */ p_distri->addr_cnt = 0; /* initialize mapping information */ for (i = 0; i < 3; i++) { p_distri->mapping[i].base = 0.0; p_distri->mapping[i].gamma = 1.0; p_distri->mapping[i].factor = 1.0; etp_mapping_precalc(&p_distri->mapping[i]); } /* create and initialize pixel array */ p_distri->p_pixels = (etp_pixel_t *)malloc(out * pix * sizeof (etp_pixel_t)); if (!p_distri->p_pixels) { free(p_distri); if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "out of memory\n"); return -1; } for (i = 0; i < out * pix; i++) { p_distri->p_pixels[i].x = -1; p_distri->p_pixels[i].y = -1; } /* create and initialize message buffer */ p_distri->msg_len = ETP_MCUF_HDR_LEN + out * pix * 3; p_distri->p_msg_buf = (etp_u8_t *)malloc(p_distri->msg_len); if (!p_distri->p_msg_buf) { free(p_distri->p_pixels); free(p_distri); if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "out of memory\n"); return -1; } memset(p_distri->p_msg_buf, 0, p_distri->msg_len); memcpy(p_distri->p_msg_buf, etp_mcuf_hdr, ETP_MCUF_HDR_LEN); p_distri->p_msg_buf[ETP_MCUF_HDR_OFS_OUTPUTS_U16 + 0] = (etp_u8_t)(out >> 8); p_distri->p_msg_buf[ETP_MCUF_HDR_OFS_OUTPUTS_U16 + 1] = (etp_u8_t)out; p_distri->p_msg_buf[ETP_MCUF_HDR_OFS_PIXELS_U16 + 0] = (etp_u8_t)(pix >> 8); p_distri->p_msg_buf[ETP_MCUF_HDR_OFS_PIXELS_U16 + 1] = (etp_u8_t)pix; /* store pointer to distributor */ p_ctx->p_display->distri_ptrs[distri] = p_distri; /* count distributors */ p_ctx->p_display->distri_cnt++; return 0; } /** * \brief process distributor address from config file * * \param[in,out] p_ctx context information * \param[in] p_setting_part2 second half of setting to process * \param[in] p_value value of setting * \return 0 in case of success, -1 in case of error */ int etp_config_proc_distri_addr(etp_config_ctx_t *p_ctx, char *p_setting_part2, char *p_value) { char *ptr; unsigned long val; unsigned int distri; etp_distri_t *p_distri; /* get distributor number */ val = strtoul(p_setting_part2, &ptr, 0); if (ptr == p_setting_part2 || *ptr != 0) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid mapping specifier \"%s\"" " in line %u of config file\n", p_setting_part2, p_ctx->line_no); return -1; } distri = (unsigned int)val; if (distri >= ETP_DISTRI_MAX_CNT) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid distributor number \"%u\"" " in line %u of config file\n", distri, p_ctx->line_no); return -1; } /* get distributor */ p_distri = p_ctx->p_display->distri_ptrs[distri]; if (!p_distri) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "no distributor with number \"%u\"" " in line %u of config file\n", distri, p_ctx->line_no); return -1; } /* check if maximum number of addresses reached */ if (p_distri->addr_cnt >= ETP_DISTRI_MAX_ADDRS) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "too many addresses (\"%s\") for distributor with" " number \"%u\" in line %u of config file\n", p_value, distri, p_ctx->line_no); return -1; } /* get address and add it to distributor */ if (etp_parse_addr(p_value, &p_distri->addrs[p_distri->addr_cnt])) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid address \"%s\" for distributor with number" " \"%u\" in line %u of config file\n", p_value, distri, p_ctx->line_no); return -1; } ++p_distri->addr_cnt; return 0; } /** * \brief process mapping from config file * * \param[in,out] p_ctx context information * \param[in] p_setting_part2 second half of setting to process * \param[in] p_value value of setting * \return 0 in case of success, -1 in case of error */ int etp_config_proc_mapping(etp_config_ctx_t *p_ctx, char *p_setting_part2, char *p_value) { char *ptr, *ptr2; unsigned long val; unsigned int distri, chan; etp_distri_t *p_distri; double base, factor, gamma; /* get distributor number */ val = strtoul(p_setting_part2, &ptr, 0); if (ptr == p_setting_part2 || (*ptr != 0 && *ptr != ' ' && *ptr != '\t' && *ptr != '\r' && *ptr != '\n')) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid mapping specifier \"%s\"" " in line %u of config file\n", p_setting_part2, p_ctx->line_no); return -1; } distri = (unsigned int)val; if (distri >= ETP_DISTRI_MAX_CNT) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid distributor number \"%u\"" " in line %u of config file\n", distri, p_ctx->line_no); return -1; } /* get channel */ while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') ptr++; if (!strcmp(ptr, "red")) chan = 0; else if (!strcmp(ptr, "green")) chan = 1; else if (!strcmp(ptr, "blue")) chan = 2; else { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid channel \"%s\"" " in line %u of config file\n", ptr, p_ctx->line_no); return -1; } /* get distributor */ p_distri = p_ctx->p_display->distri_ptrs[distri]; if (!p_distri) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "no distributor with number \"%u\"" " in line %u of config file\n", distri, p_ctx->line_no); return -1; } /* get mapping parameters: base, factor, gamma */ base = etp_strtod_noloc(p_value, &ptr); if (ptr == p_value || (*ptr != 0 && *ptr != ' ' && *ptr != '\t' && *ptr != '\r' && *ptr != '\n')) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid mapping parameters \"%s\"" " in line %u of config file\n", p_value, p_ctx->line_no); return -1; } factor = etp_strtod_noloc(ptr, &ptr2); if (ptr2 == ptr || (*ptr2 != 0 && *ptr2 != ' ' && *ptr2 != '\t' && *ptr2 != '\r' && *ptr2 != '\n')) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid mapping parameters \"%s\"" " in line %u of config file\n", p_value, p_ctx->line_no); return -1; } gamma = etp_strtod_noloc(ptr2, &ptr); if (ptr == ptr2 || (*ptr != 0 && *ptr != ' ' && *ptr != '\t' && *ptr != '\r' && *ptr != '\n')) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid mapping parameters \"%s\"" " in line %u of config file\n", p_value, p_ctx->line_no); return -1; } if (gamma <= 0.0) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid gamma value \"%f\"" " in line %u of config file\n", gamma, p_ctx->line_no); return -1; } /* store new mapping parameters and re-calculate mapping table */ p_distri->mapping[chan].base = base; p_distri->mapping[chan].factor = factor; p_distri->mapping[chan].gamma = gamma; etp_mapping_precalc(&p_distri->mapping[chan]); return 0; } /** * \brief process pixel from config file * * \param[in,out] p_ctx context information * \param[in] sz_pixel text of pixel to process * \param[in] distri number of distributor * \param[in] out number of output * \param[in] pix number of pixel * \return 0 in case of success, -1 in case of error */ int etp_config_proc_pixel(etp_config_ctx_t *p_ctx, char *sz_pixel, unsigned int distri, unsigned int out, unsigned int pix) { etp_distri_t *p_distri = p_ctx->p_display->distri_ptrs[distri]; char *ptr; unsigned int x, y, idx; /* get coordinates of pixel */ if (etp_parse_two_nos(sz_pixel, &x, &y, &ptr) || *ptr != 0 || (int)x < 0 || (int)x >= p_ctx->p_display->size.x || (int)y < 0 || (int)y >= p_ctx->p_display->size.y) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid pixel \"%s\"" " in line %u of config file\n", sz_pixel, p_ctx->line_no); return -1; } /* check pixel number */ if (pix >= ETP_PIXEL_MAX_CNT || pix >= p_distri->pixel_cnt) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "too many pixels (more than %u)" " in line %u of config file\n", p_distri->pixel_cnt, p_ctx->line_no); return -1; } /* check that pixel is not yet set */ idx = out * p_distri->pixel_cnt + pix; if (p_distri->p_pixels[idx].x >= 0 && p_distri->p_pixels[idx].y >= 0) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "pixel %u of output %u of distributor %u already set" " to pixel %u,%u in line %u of config file\n", pix, out, distri, p_distri->p_pixels[idx].x, p_distri->p_pixels[idx].y, p_ctx->line_no); return -1; } /* set pixel coordinates */ p_distri->p_pixels[idx].x = x; p_distri->p_pixels[idx].y = y; /* count pixels in total */ p_ctx->p_display->pixel_cnt++; return 0; } /** * \brief process output from config file * * \param[in,out] p_ctx context information * \param[in] p_setting_part2 second half of setting to process * \param[in] p_value value of setting * \return 0 in case of success, -1 in case of error */ int etp_config_proc_output(etp_config_ctx_t *p_ctx, char *p_setting_part2, char *p_value) { char *ptr, *p_pos, *p_white, white; unsigned int distri, out, pix; etp_distri_t *p_distri; int err; /* get number of distributor and output */ if (etp_parse_two_nos(p_setting_part2, &distri, &out, &ptr) || *ptr != 0) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid output specifier \"%s\"" " in line %u of config file\n", p_setting_part2, p_ctx->line_no); return -1; } if (distri >= ETP_DISTRI_MAX_CNT) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid distributor number \"%u\"" " in line %u of config file\n", distri, p_ctx->line_no); return -1; } if (out >= ETP_OUTPUT_MAX_CNT) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid output number \"%u\"" " in line %u of config file\n", out, p_ctx->line_no); return -1; } /* get distributor */ p_distri = p_ctx->p_display->distri_ptrs[distri]; if (!p_distri) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "no distributor with number \"%u\"" " in line %u of config file\n", distri, p_ctx->line_no); return -1; } /* check output number */ if (out >= p_distri->output_cnt) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "no output with output number \"%u\"" " in line %u of config file\n", out, p_ctx->line_no); return -1; } /* count outputs */ p_ctx->p_display->output_cnt++; /* process pixels */ /* process all parts separated by whitespace */ p_pos = p_value; err = 0; pix = 0; while (*p_pos != 0) { /* get end of item (first whitespace) */ for (p_white = p_pos; *p_white && *p_white != ' ' && *p_white != '\t' && *p_white != '\r' && *p_white != '\n'; p_white++); /* process item: a pixel */ white = *p_white; /* terminate item */ *p_white = 0; if (etp_config_proc_pixel(p_ctx, p_pos, distri, out, pix)) /* process */ err = -1; /* remember errors */ pix++; /* count pixels */ *p_white = white; /* undo termination */ /* skip whitespace and continue after it */ for (; *p_white == ' ' || *p_white == '\t' || *p_white == '\r' || *p_white == '\n'; p_white++); p_pos = p_white; } /* while (*p_pos != 0) */ return err; } /** * \brief process setting from config file * * \param[in,out] p_ctx context information * \param[in] p_setting setting to process * \param[in] p_value value of setting * \return 0 in case of success, -1 in case of error */ int etp_config_proc_setting(etp_config_ctx_t *p_ctx, char *p_setting, char *p_value) { char *ptr; unsigned int x, y; /* replace all whitespace with spaces in setting */ while ((ptr = strchr(p_setting, '\t'))) *ptr = ' '; while ((ptr = strchr(p_setting, '\r'))) *ptr = ' '; while ((ptr = strchr(p_setting, '\n'))) *ptr = ' '; /* bind address for UDP output */ if (!strcmp(p_setting, "bindAddr")) { if (etp_parse_addr(p_value, &p_ctx->p_display->bind_addr)) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid address \"%s\" for \"%s\"" " in line %u of config file\n", p_value, p_setting, p_ctx->line_no); return -1; } if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_info, "bind address: %s\n", p_value); return 0; } /* size of display */ if (!strcmp(p_setting, "size")) { if (etp_parse_two_nos(p_value, &x, &y, &ptr) || (int)x <= 0 || (int)y <= 0) { if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_err, "invalid value \"%s\" for \"%s\"" " in line %u of config file\n", p_value, p_setting, p_ctx->line_no); return -1; } p_ctx->p_display->size.x = x; p_ctx->p_display->size.y = y; return 0; } /* distributor */ if (!strncmp(p_setting, "distributor ", 12)) return etp_config_proc_distri(p_ctx, p_setting + 12, p_value); /* distributor address */ if (!strncmp(p_setting, "distributorAddr ", 16)) return etp_config_proc_distri_addr(p_ctx, p_setting + 16, p_value); /* mapping */ if (!strncmp(p_setting, "mapping ", 8)) return etp_config_proc_mapping(p_ctx, p_setting + 8, p_value); /* output */ if (!strncmp(p_setting, "output ", 7)) return etp_config_proc_output(p_ctx, p_setting + 7, p_value); /* unknown setting */ if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_warn, "unknown setting \"%s\"" " in line %u of config file, ignored\n", p_setting, p_ctx->line_no); return 0; } /** * \brief process line from config file * * \param[in,out] p_ctx context information * \param[in] p_line line to process * \return 0 in case of success, -1 in case of error */ int etp_config_proc_line(etp_config_ctx_t *p_ctx, char *p_line) { char *p_hash, *p_equal, *p_setting, *p_value; int i; /* remove comment */ p_hash = strchr(p_line, '#'); if (p_hash) *p_hash = 0; /* remove trailing whitespace */ for (i = strlen(p_line) - 1; i >= 0 && (p_line[i] == ' ' || p_line[i] == '\t' || p_line[i] == '\r' || p_line[i] == '\n'); i--) p_line[i] = 0; /* remove leading whitespace */ for (; *p_line == ' ' || *p_line == '\t' || *p_line == '\r' || *p_line == '\n'; p_line++); /* ignore empty line */ if (!p_line[0]) return 0; /* find equal sign */ p_equal = strchr(p_line, '='); if (!p_equal) { /* no equal sign found */ if (p_ctx->p_msg_func) p_ctx->p_msg_func(p_ctx->p_msg_ctx, etp_msg_type_warn, "invalid line %u in config file, ignored\n", p_ctx->line_no); return 0; } /* split string at equal sign */ *p_equal = 0; p_setting = p_line; p_value = p_equal + 1; /* remove trailing whitespaces in setting name */ for (i = strlen(p_setting) - 1; i >= 0 && (p_setting[i] == ' ' || p_setting[i] == '\t' || p_setting[i] == '\r' || p_setting[i] == '\n'); i--) p_setting[i] = 0; /* remove leading whitespaces in value */ for (; *p_value == ' ' || *p_value == '\t' || *p_value == '\r' || *p_value == '\n'; p_value++); /* process setting */ return etp_config_proc_setting(p_ctx, p_setting, p_value); } /** * \brief set default addresses of distributors * \param[in,out] p_ctx context information */ static void etp_config_set_def_distri_addrs(etp_config_ctx_t *p_ctx) { unsigned int distri; etp_distri_t *p_distri; /* set default addresses of distributors */ for (distri = 0; distri < ETP_DISTRI_MAX_CNT; distri++) { p_distri = p_ctx->p_display->distri_ptrs[distri]; if (p_distri) { /* no address yet -> use default */ if (p_distri->addr_cnt == 0) { p_distri->addr_cnt = 1; p_distri->addrs[0].sin_family = AF_INET; p_distri->addrs[0].sin_addr.s_addr = htonl(ETP_DEST_IP_BASE + p_distri->distri * ETP_DEST_IP_STEP); p_distri->addrs[0].sin_port = htons(ETP_DEST_PORT); } } // if (p_distri) } // for (distri ...) } /** * \brief process config file * * \param[in,out] p_display display to configure * \param[in] sz_config_file name of config file to read * \param[in] p_msg_func message callback function or NULL * \param[in] p_msg_ctx user context for message callback * \return 0 in case of success, -1 in case of error */ int etp_config_proc_file(etp_display_t *p_display, const char *sz_config_file, etp_msg_func_p_t p_msg_func, void *p_msg_ctx) { etp_config_ctx_t ctx; FILE *file; /* set up context */ ctx.p_display = p_display; ctx.p_msg_func = p_msg_func; ctx.p_msg_ctx = p_msg_ctx; /* check if file is present */ if (!sz_config_file || !sz_config_file[0]) { if (p_msg_func) p_msg_func(p_msg_ctx, etp_msg_type_err, "no config file specified\n"); return -1; } if (p_msg_func) p_msg_func(p_msg_ctx, etp_msg_type_info, "using config file \"%s\"\n", sz_config_file); /* open file */ file = fopen(sz_config_file, "rt"); if (!file) { if (p_msg_func) { char errmsg[256]; strerror_r(errno, errmsg, sizeof(errmsg)); p_msg_func(p_msg_ctx, etp_msg_type_err, "cannot open config file \"%s\" for reading: %s\n", sz_config_file, errmsg); } return -1; } /* process lines in file */ ctx.line_no = 1; while (!feof(file)) { char line[4096], *p_lf; /* read a line */ line[0] = 0; if (fgets(line, sizeof (line), file) == NULL) break; p_lf = strchr(line, '\n'); /* find LF in line in replace it with 0 */ if (p_lf) *p_lf = 0; else { /* no LF in line */ if (!feof(file)) { /* not at end of file */ char dummy[4096]; if (p_msg_func) p_msg_func(p_msg_ctx, etp_msg_type_warn, "line %u too long in config file, truncated\n", ctx.line_no); /* jump over in rest of line */ dummy[0] = 0; while (!feof(file) && !strchr(dummy, '\n')) if (fgets(dummy, sizeof (dummy), file) == NULL) break; } } /* process line from file */ if (etp_config_proc_line(&ctx, line)) return -1; ctx.line_no++; } /* close file */ fclose(file); /* set default addresses of distributors */ etp_config_set_def_distri_addrs(&ctx); if (p_msg_func) p_msg_func(p_msg_ctx, etp_msg_type_info, "%ux%u input format, %u distributors, %u outputs, %lu pixels\n", p_display->size.x, p_display->size.y, p_display->distri_cnt, p_display->output_cnt, p_display->pixel_cnt); return 0; }