void gl_shader_unref(struct gl_shader *shader) { if (!shader || !shader->ref || --shader->ref) return; llog_debug(shader, "free shader"); glDeleteProgram(shader->program); glDeleteShader(shader->fshader); glDeleteShader(shader->vshader); free(shader); }
SHL_EXPORT void uvt_ctx_unref(struct uvt_ctx *ctx) { if (!ctx || !ctx->ref || --ctx->ref) return; llog_debug(ctx, "free ctx %p", ctx); shl_flagset_free(ctx->minors); free(ctx->cuse_file); ev_eloop_unref(ctx->eloop); free(ctx); }
SHL_EXPORT int uvt_ctx_new(struct uvt_ctx **out, uvt_log_t log, void *log_data) { struct uvt_ctx *ctx; int ret; if (!out) return llog_dEINVAL(log, log_data); ctx = malloc(sizeof(*ctx)); if (!ctx) return llog_dENOMEM(log, log_data); memset(ctx, 0, sizeof(*ctx)); ctx->ref = 1; ctx->llog = log; ctx->llog_data = log_data; /* Default major/minor uses the TTY_MAJOR number with an offset of 2^15 * to avoid ID-clashes with any in-kernel TTY driver. As kernel drivers * use static IDs only, a lower number would be fine, too, but lets be * safe and just use high numbers. */ ctx->major = TTY_MAJOR; ctx->minor_offset = 16384; llog_debug(ctx, "new ctx %p", ctx); ret = ev_eloop_new(&ctx->eloop, ctx->llog, ctx->llog_data); if (ret) goto err_free; ctx->cuse_file = strdup("/dev/cuse"); if (!ctx->cuse_file) { ret = llog_ENOMEM(ctx); goto err_eloop; } ret = shl_flagset_new(&ctx->minors); if (ret) goto err_file; *out = ctx; return 0; err_file: free(ctx->cuse_file); err_eloop: ev_eloop_unref(ctx->eloop); err_free: free(ctx); return ret; }
SHL_EXPORT int tsm_screen_new(struct tsm_screen **out, tsm_log_t log, void *log_data) { struct tsm_screen *con; int ret; unsigned int i; if (!out) return -EINVAL; con = malloc(sizeof(*con)); if (!con) return -ENOMEM; memset(con, 0, sizeof(*con)); con->ref = 1; con->llog = log; con->llog_data = log_data; con->age_cnt = 1; con->age = con->age_cnt; con->def_attr.fr = 255; con->def_attr.fg = 255; con->def_attr.fb = 255; ret = tsm_symbol_table_new(&con->sym_table); if (ret) goto err_free; ret = tsm_screen_resize(con, 80, 24); if (ret) goto err_free; llog_debug(con, "new screen"); *out = con; return 0; err_free: for (i = 0; i < con->line_num; ++i) { line_free(con->main_lines[i]); line_free(con->alt_lines[i]); } free(con->main_lines); free(con->alt_lines); free(con->tab_ruler); tsm_symbol_table_unref(con->sym_table); free(con); return ret; }
void tsm_screen_unref(struct tsm_screen *con) { unsigned int i; if (!con || !con->ref || --con->ref) return; llog_debug(con, "destroying screen"); for (i = 0; i < con->line_num; ++i) line_free(con->lines[i]); free(con->lines); free(con->tab_ruler); shl_timer_free(con->timer); free(con); }
int tsm_screen_new(struct tsm_screen **out, tsm_log_t log) { struct tsm_screen *con; int ret; unsigned int i; if (!out) return -EINVAL; con = malloc(sizeof(*con)); if (!con) return -ENOMEM; memset(con, 0, sizeof(*con)); con->ref = 1; con->llog = log; con->def_attr.fr = 255; con->def_attr.fg = 255; con->def_attr.fb = 255; ret = shl_timer_new(&con->timer); if (ret) goto err_free; ret = tsm_screen_resize(con, 80, 24); if (ret) goto err_timer; llog_debug(con, "new screen"); *out = con; return 0; err_timer: shl_timer_free(con->timer); for (i = 0; i < con->line_num; ++i) { line_free(con->main_lines[i]); line_free(con->alt_lines[i]); } free(con->main_lines); free(con->alt_lines); free(con->tab_ruler); err_free: free(con); return ret; }
SHL_EXPORT void tsm_screen_unref(struct tsm_screen *con) { unsigned int i; if (!con || !con->ref || --con->ref) return; llog_debug(con, "destroying screen"); for (i = 0; i < con->line_num; ++i) { line_free(con->main_lines[i]); line_free(con->alt_lines[i]); } free(con->main_lines); free(con->alt_lines); free(con->tab_ruler); tsm_symbol_table_unref(con->sym_table); free(con); }
SHL_EXPORT tsm_age_t tsm_screen_draw(struct tsm_screen *con, tsm_screen_draw_cb draw_cb, void *data) { unsigned int cur_x, cur_y; unsigned int i, j, k; struct line *iter, *line = NULL; struct cell *cell, empty; struct tsm_screen_attr attr; int ret, warned = 0; const uint32_t *ch; size_t len; bool in_sel = false, sel_start = false, sel_end = false; bool was_sel = false; tsm_age_t age; if (!con || !draw_cb) return 0; screen_cell_init(con, &empty); cur_x = con->cursor_x; if (con->cursor_x >= con->size_x) cur_x = con->size_x - 1; cur_y = con->cursor_y; if (con->cursor_y >= con->size_y) cur_y = con->size_y - 1; /* push each character into rendering pipeline */ iter = con->sb_pos; k = 0; if (con->sel_active) { if (!con->sel_start.line && con->sel_start.y == SELECTION_TOP) in_sel = !in_sel; if (!con->sel_end.line && con->sel_end.y == SELECTION_TOP) in_sel = !in_sel; if (con->sel_start.line && (!iter || con->sel_start.line->sb_id < iter->sb_id)) in_sel = !in_sel; if (con->sel_end.line && (!iter || con->sel_end.line->sb_id < iter->sb_id)) in_sel = !in_sel; } for (i = 0; i < con->size_y; ++i) { if (iter) { line = iter; iter = iter->next; } else { line = con->lines[k]; k++; } if (con->sel_active) { if (con->sel_start.line == line || (!con->sel_start.line && con->sel_start.y == k - 1)) sel_start = true; else sel_start = false; if (con->sel_end.line == line || (!con->sel_end.line && con->sel_end.y == k - 1)) sel_end = true; else sel_end = false; was_sel = false; } for (j = 0; j < con->size_x; ++j) { if (j < line->size) cell = &line->cells[j]; else cell = ∅ memcpy(&attr, &cell->attr, sizeof(attr)); if (con->sel_active) { if (sel_start && j == con->sel_start.x) { was_sel = in_sel; in_sel = !in_sel; } if (sel_end && j == con->sel_end.x) { was_sel = in_sel; in_sel = !in_sel; } } if (k == cur_y + 1 && j == cur_x && !(con->flags & TSM_SCREEN_HIDE_CURSOR)) attr.underline = !attr.underline; //attr.inverse = !attr.inverse; /* TODO: do some more sophisticated inverse here. When * INVERSE mode is set, we should instead just select * inverse colors instead of switching background and * foreground */ if (con->flags & TSM_SCREEN_INVERSE) attr.inverse = !attr.inverse; if (in_sel || was_sel) { was_sel = false; attr.inverse = !attr.inverse; } if (con->age_reset) { age = 0; } else { age = cell->age; if (line->age > age) age = line->age; if (con->age > age) age = con->age; } ch = tsm_symbol_get(con->sym_table, &cell->ch, &len); if (cell->ch == ' ' || cell->ch == 0) len = 0; ret = draw_cb(con, cell->ch, ch, len, cell->width, j, i, &attr, age, data); if (ret && warned++ < 3) { llog_debug(con, "cannot draw glyph at %ux%u via text-renderer", j, i); if (warned == 3) llog_debug(con, "suppressing further warnings during this rendering round"); } } } if (con->age_reset) { con->age_reset = 0; return 0; } else { return con->age_cnt; } }
void tsm_screen_draw(struct tsm_screen *con, tsm_screen_prepare_cb prepare_cb, tsm_screen_draw_cb draw_cb, tsm_screen_render_cb render_cb, void *data) { unsigned int cur_x, cur_y; unsigned int i, j, k; struct line *iter, *line = NULL; struct cell *cell; struct tsm_screen_attr attr; bool cursor_done = false; int ret, warned = 0; uint64_t time_prep = 0, time_draw = 0, time_rend = 0; const uint32_t *ch; size_t len; struct cell empty; if (!con || !draw_cb) return; cell_init(con, &empty); cur_x = con->cursor_x; if (con->cursor_x >= con->size_x) cur_x = con->size_x - 1; cur_y = con->cursor_y; if (con->cursor_y >= con->size_y) cur_y = con->size_y - 1; /* render preparation */ if (prepare_cb) { if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) shl_timer_reset(con->timer); ret = prepare_cb(con, data); if (ret) { llog_warning(con, "cannot prepare text-renderer for rendering"); return; } if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) time_prep = shl_timer_elapsed(con->timer); } else { time_prep = 0; } /* push each character into rendering pipeline */ if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) shl_timer_reset(con->timer); iter = con->sb_pos; k = 0; for (i = 0; i < con->size_y; ++i) { if (iter) { line = iter; iter = iter->next; } else { line = con->lines[k]; k++; } for (j = 0; j < con->size_x; ++j) { if (j < line->size) cell = &line->cells[j]; else cell = ∅ memcpy(&attr, &cell->attr, sizeof(attr)); if (k == cur_y + 1 && j == cur_x) { cursor_done = true; if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) attr.inverse = !attr.inverse; } /* TODO: do some more sophisticated inverse here. When * INVERSE mode is set, we should instead just select * inverse colors instead of switching background and * foreground */ if (con->flags & TSM_SCREEN_INVERSE) attr.inverse = !attr.inverse; ch = tsm_symbol_get(NULL, &cell->ch, &len); if (cell->ch == ' ' || cell->ch == 0) len = 0; ret = draw_cb(con, cell->ch, ch, len, j, i, &attr, data); if (ret && warned++ < 3) { llog_debug(con, "cannot draw glyph at %ux%u via text-renderer", j, i); if (warned == 3) llog_debug(con, "suppressing further warnings during this rendering round"); } } if (k == cur_y + 1 && !cursor_done) { cursor_done = true; if (!(con->flags & TSM_SCREEN_HIDE_CURSOR)) { if (!(con->flags & TSM_SCREEN_INVERSE)) attr.inverse = !attr.inverse; draw_cb(con, 0, NULL, 0, cur_x, i, &attr, data); } } } if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) time_draw = shl_timer_elapsed(con->timer); /* perform final rendering steps */ if (render_cb) { if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) shl_timer_reset(con->timer); ret = render_cb(con, data); if (ret) llog_warning(con, "cannot render via text-renderer"); if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) time_rend = shl_timer_elapsed(con->timer); } else { time_rend = 0; } if (con->opts & TSM_SCREEN_OPT_RENDER_TIMING) llog_debug(con, "timing: sum: %" PRIu64 " prepare: %" PRIu64 " draw: %" PRIu64 " render: %" PRIu64, time_prep + time_draw + time_rend, time_prep, time_draw, time_rend); }
int gl_shader_new(struct gl_shader **out, const char *vert, const char *frag, char **attr, size_t attr_count, llog_submit_t llog) { struct gl_shader *shader; int ret, i; char msg[512]; GLint status = 1; if (!out || !vert || !frag) return -EINVAL; shader = malloc(sizeof(*shader)); if (!shader) return -ENOMEM; memset(shader, 0, sizeof(*shader)); shader->ref = 1; shader->llog = llog; llog_debug(shader, "new shader"); shader->vshader = compile_shader(shader, GL_VERTEX_SHADER, vert); if (shader->vshader == GL_NONE) { ret = -EFAULT; goto err_free; } shader->fshader = compile_shader(shader, GL_FRAGMENT_SHADER, frag); if (shader->fshader == GL_NONE) { ret = -EFAULT; goto err_vshader; } shader->program = glCreateProgram(); glAttachShader(shader->program, shader->vshader); glAttachShader(shader->program, shader->fshader); for (i = 0; i < attr_count; ++i) glBindAttribLocation(shader->program, i, attr[i]); glLinkProgram(shader->program); glGetProgramiv(shader->program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { msg[0] = 0; glGetProgramInfoLog(shader->program, sizeof(msg), NULL, msg); llog_warning(shader, "cannot link shader: %s", msg); ret = -EFAULT; goto err_link; } if (gl_has_error(shader)) { llog_warning(shader, "shader creation failed"); ret = -EFAULT; goto err_link; } *out = shader; return 0; err_link: glDeleteProgram(shader->program); glDeleteShader(shader->fshader); err_vshader: glDeleteShader(shader->vshader); err_free: free(shader); return ret; }