vtgl.c (41239B)
1 /* See LICENSE for copyright details */ 2 /* TODO: define this ourselves since we should be loading it at runtime */ 3 #define GL_GLEXT_PROTOTYPES 4 #include <GL/glcorearb.h> 5 6 #include "vtgl.h" 7 8 #include "config.h" 9 10 #include "font.c" 11 #include "terminal.c" 12 13 #define LABEL_GL_OBJECT(type, id, s) {s8 _s = (s); glObjectLabel(type, id, _s.len, (c8 *)_s.data);} 14 15 #define REVERSE_VIDEO_MASK (Colour){.r = 0xff, .g = 0xff, .b = 0xff}.rgba 16 17 #define VERTEX_SHADER_TEXT \ 18 "#version 430 core\n" \ 19 "\n" \ 20 "layout(location = 0) in vec2 vertex_position;\n" \ 21 "layout(location = 1) in vec2 vertex_texture_coordinate;\n" \ 22 "layout(location = 2) in vec4 vertex_colour;\n" \ 23 "\n" \ 24 "layout(location = 0) out vec2 fragment_texture_coordinate;\n" \ 25 "layout(location = 1) out vec4 fragment_colour;\n" \ 26 "\n" \ 27 "layout(location = 0) uniform mat4 u_Pmat;\n" \ 28 "\n" \ 29 "void main()\n" \ 30 "{\n" \ 31 " fragment_texture_coordinate = vertex_texture_coordinate;\n" \ 32 " fragment_colour = vertex_colour;\n" \ 33 "\n" \ 34 " gl_Position = u_Pmat * vec4(vertex_position, 0.0, 1.0);\n" \ 35 "}\n" 36 37 static void 38 set_projection_matrix(GLCtx *gl, u32 stage) 39 { 40 f32 w = gl->window_size.w; 41 f32 h = gl->window_size.h; 42 43 f32 pmat[4 * 4] = { 44 2.0/w, 0.0, 0.0, -1.0, 45 0.0, 2.0/h, 0.0, -1.0, 46 0.0, 0.0, -1.0, 0.0, 47 0.0, 0.0, 0.0, 1.0, 48 }; 49 50 glProgramUniformMatrix4fv(gl->programs[stage], SHADER_PMAT_LOC, 1, GL_TRUE, pmat); 51 } 52 53 static u32 54 compile_shader(Arena a, u32 type, s8 shader) 55 { 56 u32 sid = glCreateShader(type); 57 58 glShaderSource(sid, 1, (const char **)&shader.data, (int *)&shader.len); 59 glCompileShader(sid); 60 61 i32 res = 0; 62 glGetShaderiv(sid, GL_COMPILE_STATUS, &res); 63 if (res != GL_TRUE) { 64 i32 len; 65 glGetShaderiv(sid, GL_INFO_LOG_LENGTH, &len); 66 s8 err = s8alloc(&a, len); 67 glGetShaderInfoLog(sid, len, (int *)&err.len, (char *)err.data); 68 os_write_err_msg(s8("compile_shader: ")); 69 os_write_err_msg(err); 70 glDeleteShader(sid); 71 return 0; 72 } 73 74 return sid; 75 } 76 77 static u32 78 program_from_shader_text(s8 vertex, s8 fragment, Arena a) 79 { 80 u32 pid = glCreateProgram(); 81 82 u32 vid = compile_shader(a, GL_VERTEX_SHADER, vertex); 83 if (vid == 0) { 84 glDeleteProgram(pid); 85 return 0; 86 } 87 88 u32 fid = compile_shader(a, GL_FRAGMENT_SHADER, fragment); 89 if (fid == 0) { 90 glDeleteShader(vid); 91 glDeleteProgram(pid); 92 return 0; 93 } 94 95 glAttachShader(pid, vid); 96 glAttachShader(pid, fid); 97 glLinkProgram(pid); 98 glValidateProgram(pid); 99 glUseProgram(pid); 100 glDeleteShader(vid); 101 glDeleteShader(fid); 102 103 return pid; 104 } 105 106 typedef struct { 107 Term *t; 108 u8 *path; 109 s8 info; 110 u32 stage; 111 } queue_shader_reload_ctx; 112 113 static void 114 update_uniforms(GLCtx *gl, enum shader_stages stage) 115 { 116 switch (stage) { 117 case SHADER_RENDER: 118 case SHADER_RECTS: 119 break; 120 case SHADER_POST: 121 #define X(name) gl->post.name = glGetUniformLocation(gl->programs[stage], "u_" #name); 122 GL_POST_UNIFORMS 123 #undef X 124 break; 125 case SHADER_COUNT: ASSERT(0); break; 126 } 127 } 128 129 static void 130 reload_shader(GLCtx *gl, PlatformAPI *platform, u8 *path, u32 stage, s8 info, Arena a) 131 { 132 s8 fs_text = platform->read_file(path, &a); 133 if (fs_text.len) { 134 u32 program = program_from_shader_text(s8(VERTEX_SHADER_TEXT), fs_text, a); 135 if (program) { 136 glDeleteProgram(gl->programs[stage]); 137 gl->programs[stage] = program; 138 update_uniforms(gl, stage); 139 set_projection_matrix(gl, stage); 140 } 141 } 142 if (info.len) os_write_err_msg(info); 143 } 144 145 static s8 fs_name[SHADER_COUNT] = { 146 [SHADER_RENDER] = s8("frag_render.glsl"), 147 [SHADER_RECTS] = s8("frag_for_rects.glsl"), 148 [SHADER_POST] = s8("frag_post.glsl"), 149 }; 150 151 static void 152 reload_all_shaders(GLCtx *gl, PlatformAPI *platform, Arena a) 153 { 154 Stream fs_path = stream_alloc(&a, KB(4)); 155 stream_push_s8(&fs_path, g_shader_path_prefix); 156 if (fs_path.widx && fs_path.buf[fs_path.widx - 1] != platform->path_separator) 157 stream_push_byte(&fs_path, platform->path_separator); 158 159 i32 sidx = fs_path.widx; 160 for (u32 i = 0; i < SHADER_COUNT; i++) { 161 stream_push_s8(&fs_path, fs_name[i]); 162 stream_push_byte(&fs_path, 0); 163 reload_shader(gl, platform, fs_path.buf, i, (s8){0}, a); 164 fs_path.widx = sidx; 165 } 166 167 os_write_err_msg(s8("Reloaded Shaders\n")); 168 } 169 170 static PLATFORM_FILE_WATCH_CALLBACK_FN(queue_shader_reload) 171 { 172 queue_shader_reload_ctx *ctx = user_ctx; 173 ctx->path = path; 174 work_queue_insert(ctx->t, WQ_RELOAD_SHADER, ctx); 175 } 176 177 static v4 178 normalize_colour(Colour c) 179 { 180 return (v4){.r = c.r / 255.0f, .g = c.g / 255.0f, .b = c.b / 255.0f, .a = c.a / 255.0f}; 181 } 182 183 static void 184 clear_colour(void) 185 { 186 Colour c = g_colours.data[g_colours.bgidx]; 187 v4 cc = normalize_colour(c); 188 glClearColor(cc.r, cc.g, cc.b, cc.a); 189 glClear(GL_COLOR_BUFFER_BIT); 190 } 191 192 static v2 193 get_cell_size(FontAtlas *fa) 194 { 195 v2 result = {.w = fa->info.w, .h = fa->info.h}; 196 return result; 197 } 198 199 static v2 200 get_occupied_size(Term *t) 201 { 202 v2 cs = get_cell_size(&t->fa); 203 v2 result = {.x = t->size.w * cs.w, .y = t->size.h * cs.h}; 204 return result; 205 } 206 207 static v2 208 get_terminal_top_left(Term *t) 209 { 210 v2 os = get_occupied_size(t); 211 v2 delta = {.x = t->gl.window_size.w - os.w, .y = t->gl.window_size.h - os.h}; 212 v2 result = {.x = delta.x / 2, .y = t->gl.window_size.h - delta.y / 2}; 213 return result; 214 } 215 216 static void 217 resize_terminal(Term *t, PlatformAPI *platform, iv2 window_size) 218 { 219 v2 ws = v2_from_iv2(window_size); 220 ws.w -= 2 * g_term_margin.w; 221 ws.h -= 2 * g_term_margin.h; 222 223 iv2 old_size = t->size; 224 v2 cs = get_cell_size(&t->fa); 225 t->size.w = (i32)(ws.w / cs.w); 226 t->size.h = (i32)(ws.h / cs.h); 227 228 if (t->size.w > ARRAY_COUNT(t->tabs) * 32) { 229 t->size.w = ARRAY_COUNT(t->tabs) * 32u; 230 stream_push_s8(&t->error_stream, s8("resize: max terminal width is ")); 231 stream_push_u64(&t->error_stream, t->size.w); 232 stream_push_s8(&t->error_stream, s8("; clamping\n")); 233 os_write_err_msg(stream_to_s8(&t->error_stream)); 234 t->error_stream.widx = 0; 235 } 236 237 if (!equal_iv2(old_size, t->size)) { 238 t->size = initialize_framebuffer(&t->views[0].fb, t->size); 239 initialize_framebuffer(&t->views[1].fb, t->size); 240 t->gl.flags |= NEEDS_REFILL; 241 } 242 243 platform->set_terminal_size(t->child, t->size.h, t->size.w, ws.w, ws.h); 244 245 t->gl.flags |= RESIZE_RENDERER; 246 t->gl.flags &= ~NEEDS_RESIZE; 247 } 248 249 static void 250 resize(Term *t, PlatformAPI *platform, iv2 window_size) 251 { 252 GLCtx *gl = &t->gl; 253 gl->window_size = window_size; 254 255 glViewport(0, 0, window_size.w, window_size.h); 256 257 glActiveTexture(GL_TEXTURE0 + gl->fb_tex_unit); 258 glBindTexture(GL_TEXTURE_2D, gl->fb_tex); 259 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, window_size.w, window_size.h, 0, 260 GL_RGBA, GL_UNSIGNED_BYTE, 0); 261 262 /* NOTE: reactive the glyph texture unit */ 263 glActiveTexture(GL_TEXTURE0); 264 265 u32 buffer_size = t->size.w * t->size.h * sizeof(RenderCell); 266 glDeleteBuffers(1, &gl->render_shader_ssbo); 267 glGenBuffers(1, &gl->render_shader_ssbo); 268 glBindBuffer(GL_SHADER_STORAGE_BUFFER, gl->render_shader_ssbo); 269 glBufferData(GL_SHADER_STORAGE_BUFFER, buffer_size, 0, GL_DYNAMIC_DRAW); 270 glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, gl->render_shader_ssbo); 271 LABEL_GL_OBJECT(GL_BUFFER, gl->render_shader_ssbo, s8("RenderCells")); 272 gl->queued_render = 1; 273 274 v2 cs = get_cell_size(&t->fa); 275 276 ShaderParameters *sp = &gl->shader_parameters; 277 sp->cell_size = (iv2){.w = cs.w, .h = cs.h}; 278 sp->strike_min = (t->fa.info.baseline + 0.40 * t->fa.info.h); 279 sp->strike_max = (t->fa.info.baseline + 0.48 * t->fa.info.h); 280 sp->underline_min = (0.89 * t->fa.info.h); 281 sp->underline_max = (0.96 * t->fa.info.h); 282 sp->top_left_margin = g_term_margin; 283 sp->margin_colour = g_colours.data[g_colours.bgidx].rgba; 284 //sp->margin_colour = 0x7f003f00; 285 286 sp->term_size_in_pixels = gl->window_size; 287 sp->term_size_in_cells = t->size; 288 289 for (u32 i = 0; i < SHADER_COUNT; i++) 290 set_projection_matrix(gl, i); 291 292 gl->flags &= ~RESIZE_RENDERER; 293 } 294 295 static RenderCtx 296 make_render_ctx(Arena *a, GLCtx *gl, FontAtlas *fa) 297 { 298 RenderCtx result; 299 result.gl = gl; 300 result.fa = fa; 301 result.rpb = alloc(a, RenderPushBuffer, 1); 302 result.a = sub_arena(a, MB(4)); 303 return result; 304 } 305 306 static iv2 307 get_gpu_texture_position(v2 cs, u32 gpu_tile_index) 308 { 309 uv2 gpu_position = {.x = gpu_tile_index & 0xFFFF, .y = gpu_tile_index >> 16}; 310 iv2 result = {.y = gpu_position.y * cs.y, .x = gpu_position.x * cs.x}; 311 return result; 312 } 313 314 static u32 315 get_gpu_glyph_index(Arena a, GLCtx *gl, FontAtlas *fa, u32 codepoint, u32 font_id, 316 enum face_style style, CachedGlyph **out) 317 { 318 u32 *data = render_glyph(&a, fa, codepoint, font_id, style, out); 319 if (data) { 320 CachedGlyph *cg = *out; 321 cg->uploaded_to_gpu = 1; 322 323 v2 cell_size = get_cell_size(fa); 324 iv2 glyph_position = get_gpu_texture_position(cell_size, cg->gpu_tile_index); 325 ASSERT(glyph_position.x + cell_size.w * cg->tile_count < gl->glyph_bitmap_dim.x); 326 ASSERT(glyph_position.y + cell_size.h < gl->glyph_bitmap_dim.y); 327 328 glTexSubImage2D(GL_TEXTURE_2D, 0, glyph_position.x, glyph_position.y, 329 cell_size.w * cg->tile_count, cell_size.h, 330 GL_RGBA, GL_UNSIGNED_BYTE, data); 331 } 332 ASSERT((*out)->uploaded_to_gpu); 333 return (*out)->gpu_tile_index; 334 } 335 336 /* NOTE: this function assumes we are drawing quads */ 337 static void 338 flush_render_push_buffer(RenderCtx *rc) 339 { 340 BEGIN_TIMED_BLOCK(); 341 if (rc->rpb->count > 0) { 342 u32 n = rc->rpb->count; 343 GLCtx *gl = rc->gl; 344 RenderPushBuffer *rpb = rc->rpb; 345 ASSERT((n % 4) == 0); 346 347 BEGIN_NAMED_BLOCK(upload); 348 glBindBuffer(GL_ARRAY_BUFFER, gl->vbos[0]); 349 glBufferSubData(GL_ARRAY_BUFFER, 0, n * sizeof(*rpb->positions), rpb->positions); 350 glBindBuffer(GL_ARRAY_BUFFER, gl->vbos[1]); 351 glBufferSubData(GL_ARRAY_BUFFER, 0, n * sizeof(*rpb->texture_coordinates), rpb->texture_coordinates); 352 glBindBuffer(GL_ARRAY_BUFFER, gl->vbos[2]); 353 glBufferSubData(GL_ARRAY_BUFFER, 0, n * sizeof(*rpb->colours), rpb->colours); 354 END_NAMED_BLOCK(upload); 355 356 glDrawElements(GL_TRIANGLES, 6 * n / 4, GL_UNSIGNED_INT, 0); 357 } 358 rc->rpb->count = 0; 359 END_TIMED_BLOCK(); 360 } 361 362 static u32 363 get_render_push_buffer_idx(RenderCtx *rc, u32 count) 364 { 365 if (rc->rpb->count + count > RENDER_PUSH_BUFFER_CAP) 366 flush_render_push_buffer(rc); 367 u32 result = rc->rpb->count; 368 rc->rpb->count += count; 369 return result; 370 } 371 372 static void 373 push_rect_full(RenderCtx *rc, Rect r, v4 colour, v2 min_tex_coord, v2 max_tex_coord) 374 { 375 BEGIN_TIMED_BLOCK(); 376 377 u32 idx = get_render_push_buffer_idx(rc, 4); 378 v2 start = r.pos; 379 v2 end = {.x = r.pos.x + r.size.w, .y = r.pos.y + r.size.h}; 380 381 RenderPushBuffer *rpb = rc->rpb; 382 rpb->positions[idx + 0] = (v2){.x = end.x, .y = end.y }; 383 rpb->positions[idx + 1] = (v2){.x = end.x, .y = start.y}; 384 rpb->positions[idx + 2] = (v2){.x = start.x, .y = start.y}; 385 rpb->positions[idx + 3] = (v2){.x = start.x, .y = end.y }; 386 387 rpb->texture_coordinates[idx + 0] = (v2){.x = max_tex_coord.x, .y = max_tex_coord.y}; 388 rpb->texture_coordinates[idx + 1] = (v2){.x = max_tex_coord.x, .y = min_tex_coord.y}; 389 rpb->texture_coordinates[idx + 2] = (v2){.x = min_tex_coord.x, .y = min_tex_coord.y}; 390 rpb->texture_coordinates[idx + 3] = (v2){.x = min_tex_coord.x, .y = max_tex_coord.y}; 391 392 rpb->colours[idx + 0] = colour; 393 rpb->colours[idx + 1] = colour; 394 rpb->colours[idx + 2] = colour; 395 rpb->colours[idx + 3] = colour; 396 397 END_TIMED_BLOCK(); 398 } 399 400 static void 401 push_rect_textured(RenderCtx *rc, Rect r, v4 colour, b32 flip_texture) 402 { 403 v2 min_tex_coord, max_tex_coord; 404 if (!flip_texture) { 405 max_tex_coord = (v2){.x = 1.0f, .y = 1.0f}; 406 min_tex_coord = (v2){.x = 0.0f, .y = 0.0f}; 407 } else { 408 max_tex_coord = (v2){.x = 1.0f, .y = 0.0f}; 409 min_tex_coord = (v2){.x = 0.0f, .y = 1.0f}; 410 } 411 push_rect_full(rc, r, colour, min_tex_coord, max_tex_coord); 412 } 413 414 static void 415 push_rect(RenderCtx *rc, Rect r, v4 colour) 416 { 417 f32 max_x = 1.0f / rc->gl->glyph_bitmap_dim.x; 418 f32 max_y = 1.0f / rc->gl->glyph_bitmap_dim.y; 419 push_rect_full(rc, r, colour, (v2){0}, (v2){.x = max_x, .y = max_y}); 420 } 421 422 static v2 423 push_s8(RenderCtx *rc, v2 pos, v4 colour, u32 font_id, s8 s) 424 { 425 BEGIN_TIMED_BLOCK(); 426 CachedGlyph *cg; 427 v2 start, end, text_size = {0}; 428 v2 scale = {.x = 1.0f / rc->gl->glyph_bitmap_dim.x, .y = 1.0f / rc->gl->glyph_bitmap_dim.y}; 429 430 /* TODO: implement GPU based glyph rasterizing */ 431 pos.x = (u32)pos.x; 432 pos.y = (u32)pos.y; 433 434 while (s.len) { 435 u32 cp = get_utf8(&s); 436 if (cp == (u32)-1) 437 break; 438 439 get_gpu_glyph_index(rc->a, rc->gl, rc->fa, cp, font_id, FS_NORMAL, &cg); 440 cached_glyph_to_uv(rc->fa, cg, &start, &end, scale); 441 442 Rect r = {.pos = pos, .size = {.x = cg->width, .y = cg->height}}; 443 r.pos.x += cg->x0; 444 r.pos.y -= (cg->height + cg->y0); 445 446 push_rect_full(rc, r, colour, start, end); 447 448 text_size.x += cg->advance; 449 pos.x += cg->advance; 450 if (cg->height > text_size.y) 451 text_size.y = cg->height; 452 } 453 text_size.x -= cg->advance; 454 text_size.w += cg->width; 455 456 END_TIMED_BLOCK(); 457 458 return text_size; 459 } 460 461 static v2 462 measure_text(RenderCtx *rc, u32 font_id, s8 text) 463 { 464 BEGIN_TIMED_BLOCK(); 465 466 v2 result = {0}; 467 468 CachedGlyph *cg; 469 while (text.len) { 470 u32 cp = get_utf8(&text); 471 if (cp == (u32)-1) 472 break; 473 474 get_gpu_glyph_index(rc->a, rc->gl, rc->fa, cp, font_id, FS_NORMAL, &cg); 475 if (cg->height > result.y) 476 result.y = cg->height; 477 result.x += cg->advance; 478 } 479 result.x -= cg->advance; 480 result.x += cg->width; 481 482 END_TIMED_BLOCK(); 483 484 return result; 485 } 486 487 /* TODO: this is outdated */ 488 /* NOTE: In this program we render to an offscreen render target that is later drawn 489 * to the screen as a full window quad. Therefore render_framebuffer must take care 490 * to handle all necessary padding (window and cell). Outside of here everyone should 491 * simply care about the terminal in terms of rows and columns (t->size). */ 492 static void 493 render_framebuffer(Term *t, RenderCell *render_buf, TerminalInput *input, Arena arena) 494 { 495 BEGIN_TIMED_BLOCK(); 496 497 TermView *tv = t->views + t->view_idx; 498 iv2 term_size = t->size; 499 /* NOTE: draw whole framebuffer */ 500 for (u32 row = 0; row < term_size.h; row++) { 501 for (u32 col = 0; col < term_size.w; col++) { 502 Cell c = tv->fb.rows[row][col]; 503 RenderCell *rc = render_buf + (row * term_size.w + col); 504 505 CachedGlyph *cg; 506 rc->gpu_glyph = get_gpu_glyph_index(arena, &t->gl, &t->fa, c.cp, 0, 507 c.bg & FS_MASK, &cg); 508 rc->fg = c.fg; 509 rc->bg = c.bg; 510 511 /* TODO: there is probably a better way to do this */ 512 u32 tiles = cg->tile_count; 513 if (tiles > 1) { 514 ASSERT(tiles == 2); 515 rc[1].gpu_glyph = rc->gpu_glyph + 1; 516 rc[1].fg = rc->fg; 517 rc[1].bg = rc->bg; 518 col++; 519 } 520 } 521 } 522 523 /* NOTE: draw selection if active */ 524 SelectionIterator si = selection_iterator(t->selection.range, tv->fb.rows, term_size.w); 525 for (Cell *c = selection_next(&si); c; c = selection_next(&si)) { 526 RenderCell *rc = render_buf + si.cursor.y * term_size.w + si.cursor.x; 527 rc->fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); 528 } 529 530 END_TIMED_BLOCK(); 531 } 532 533 static void 534 render_cursor(Term *t, b32 focused, Arena a) 535 { 536 BEGIN_TIMED_BLOCK(); 537 538 iv2 curs = t->cursor.pos; 539 Cell *c = &t->views[t->view_idx].fb.rows[curs.y][curs.x]; 540 RenderCell *rc = alloc(&a, RenderCell, 3); 541 542 size rc_off = 1; 543 size length = sizeof(RenderCell); 544 size offset = sizeof(RenderCell) * (curs.y * t->size.w + curs.x); 545 546 CachedGlyph *cg; 547 rc[1].gpu_glyph = get_gpu_glyph_index(a, &t->gl, &t->fa, c->cp, 0, c->bg & FS_MASK, &cg); 548 rc[1].fg = c->fg; 549 rc[1].bg = c->bg; 550 551 /* NOTE: draw cursor */ 552 if (focused && (!(t->mode.win & WM_HIDECURSOR) && t->scroll_offset == 0)) { 553 rc[1].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); 554 //if ((t->mode.term & TM_ALTSCREEN) == 0) 555 // rc[1].fg |= SHADER_PACK_ATTR(ATTR_BLINK); 556 557 if (c->bg & ATTR_WIDE) { 558 length *= 2; 559 rc[2].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); 560 //if ((t->mode.term & TM_ALTSCREEN) == 0) 561 // rc[2].fg |= SHADER_PACK_ATTR(ATTR_BLINK); 562 } else if (c->bg & ATTR_WDUMMY) { 563 rc_off = 0; 564 length *= 2; 565 offset -= sizeof(RenderCell); 566 rc[0].fg ^= SHADER_PACK_ATTR(ATTR_INVERSE); 567 //if ((t->mode.term & TM_ALTSCREEN) == 0) 568 // rc[0].fg |= SHADER_PACK_ATTR(ATTR_BLINK); 569 } 570 } 571 572 glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, length, rc + rc_off); 573 574 END_TIMED_BLOCK(); 575 } 576 577 static iv2 578 mouse_to_cell_space(Term *t, v2 mouse) 579 { 580 v2 cell_size = get_cell_size(&t->fa); 581 v2 top_left = get_terminal_top_left(t); 582 583 iv2 result; 584 result.x = (i32)((mouse.x - top_left.x) / cell_size.w + 0.5); 585 result.y = (i32)((top_left.y - mouse.y) / cell_size.h + 0.5); 586 587 CLAMP(result.x, 0, t->size.w - 1); 588 CLAMP(result.y, 0, t->size.h - 1); 589 590 return result; 591 } 592 593 static void 594 stream_push_selection(Stream *s, Row *rows, Range sel, u32 term_width) 595 { 596 s->errors |= !is_valid_range(sel); 597 if (s->errors) return; 598 599 SelectionIterator si = selection_iterator(sel, rows, term_width); 600 u32 last_non_space_idx = 0; 601 for (Cell *c = selection_next(&si); c; c = selection_next(&si)) { 602 if (c->bg & ATTR_WDUMMY) 603 continue; 604 605 stream_push_s8(s, utf8_encode(c->cp)); 606 if (!ISSPACE(c->cp)) 607 last_non_space_idx = s->widx; 608 609 if (si.next.y != si.cursor.y) { 610 s->widx = last_non_space_idx; 611 stream_push_byte(s, '\n'); 612 } 613 } 614 615 s->widx = last_non_space_idx; 616 } 617 618 static void 619 begin_selection(Term *t, u32 click_count, v2 mouse) 620 { 621 Selection *sel = &t->selection; 622 sel->state = CLAMP(click_count, SS_NONE, SS_WORDS); 623 sel->range.end = INVALID_RANGE_END; 624 625 iv2 cell = mouse_to_cell_space(t, mouse); 626 627 switch (sel->state) { 628 case SS_WORDS: sel->anchor = sel->range = get_word_around_cell(t, cell); break; 629 case SS_CHAR: sel->anchor = get_char_around_cell(t, cell); break; 630 case SS_NONE: break; 631 } 632 633 t->gl.queued_render = 1; 634 } 635 636 static void 637 update_selection(Term *t, TerminalInput *input) 638 { 639 if (!input->keys[MOUSE_LEFT].ended_down) 640 return; 641 642 if (input->mouse.x == input->last_mouse.x && input->mouse.y == input->last_mouse.y) 643 return; 644 645 Selection *sel = &t->selection; 646 iv2 new_p = mouse_to_cell_space(t, input->mouse); 647 Range new, old_range = sel->range; 648 switch (sel->state) { 649 case SS_WORDS: new = get_word_around_cell(t, new_p); break; 650 case SS_CHAR: new = get_char_around_cell(t, new_p); break; 651 case SS_NONE: /*TODO: INVALID_CODE_PATH;*/ return; break; 652 } 653 654 if (sel->anchor.start.y < new.start.y) { 655 sel->range.start = sel->anchor.start; 656 sel->range.end = new.end; 657 } else if (sel->anchor.start.y > new.start.y) { 658 sel->range.start = new.start; 659 sel->range.end = sel->anchor.end; 660 } else { 661 if (new.start.x < sel->anchor.start.x) { 662 sel->range.start = new.start; 663 sel->range.end = sel->anchor.end; 664 } else { 665 sel->range.start = sel->anchor.start; 666 sel->range.end = new.end; 667 } 668 } 669 sel->range = normalize_range(sel->range); 670 671 if (!equal_range(old_range, sel->range)) 672 t->gl.queued_render = 1; 673 } 674 675 KEYBIND_FN(copy) 676 { 677 Stream buf = arena_stream(t->arena_for_frame); 678 stream_push_selection(&buf, t->views[t->view_idx].fb.rows, t->selection.range, t->size.w); 679 platform->set_clipboard(&buf, a.i); 680 return 1; 681 } 682 683 KEYBIND_FN(paste) 684 { 685 Stream buf = arena_stream(t->arena_for_frame); 686 b32 bracketed = t->mode.win & WM_BRACKPASTE; 687 688 if (bracketed) stream_push_s8(&buf, s8("\033[200~")); 689 b32 success = platform->get_clipboard(&buf, a.i); 690 if (bracketed) stream_push_s8(&buf, s8("\033[201~")); 691 692 if (success) 693 platform->write(t->child, stream_to_s8(&buf)); 694 695 return 1; 696 } 697 698 KEYBIND_FN(scroll) 699 { 700 if (t->mode.term & TM_ALTSCREEN) 701 return 0; 702 703 TermView *tv = t->views + t->view_idx; 704 705 /* NOTE: do nothing if there aren't enough lines to scrollback */ 706 if (tv->lines.filled < t->size.h) 707 return 1; 708 709 t->scroll_offset += a.i; 710 CLAMP(t->scroll_offset, 0, tv->lines.filled - (t->size.h - 1)); 711 712 t->gl.flags |= NEEDS_REFILL; 713 714 return 1; 715 } 716 717 KEYBIND_FN(zoom) 718 { 719 shift_font_sizes(&t->fa, a.i); 720 font_atlas_update(&t->fa, t->gl.glyph_bitmap_dim); 721 t->gl.flags |= NEEDS_RESIZE; 722 return 1; 723 } 724 725 static void 726 report_mouse(Term *t, TerminalInput *input, b32 released, b32 beginning) 727 { 728 if ((t->mode.win & WM_MOUSE_X10) && released) 729 return; 730 731 iv2 pos = mouse_to_cell_space(t, input->mouse); 732 if ((pos.x > (255 - 32)) || (pos.y > (255 - 32))) 733 return; 734 735 /* TODO: pass the button into this function once they are given in order */ 736 /* TODO: extended mouse buttons (up to button 11) should also be encoded */ 737 i32 button = 0; 738 if (input->keys[MOUSE_LEFT].ended_down) button = 1; 739 else if (input->keys[MOUSE_MIDDLE].ended_down) button = 2; 740 else if (input->keys[MOUSE_RIGHT].ended_down) button = 3; 741 else if (input->mouse_scroll.y > 0) button = 4; 742 else if (input->mouse_scroll.y < 0) button = 5; 743 744 i32 value; 745 if (t->mode.win & WM_MOUSE_TRK && !beginning && !released) { 746 if (equal_iv2(t->interaction.last_cell_report, pos)) 747 return; 748 value = 32; 749 } else { 750 value = 0; 751 } 752 t->interaction.last_cell_report = pos; 753 754 if ((t->mode.win & WM_MOUSE_SGR) && !button) 755 value += 0; 756 else if (!button) 757 value += 3; 758 else if (button >= 4) 759 value += 64 + button - 4; 760 else 761 value += button - 1; 762 763 if (!(t->mode.win & WM_MOUSE_X10)) { 764 value += ((input->modifiers & MOD_SHIFT) ? 4 : 0) 765 + ((input->modifiers & MOD_ALT) ? 8 : 0) 766 + ((input->modifiers & MOD_CONTROL) ? 16 : 0); 767 } 768 769 Stream buf = arena_stream(t->arena_for_frame); 770 if (t->mode.win & WM_MOUSE_SGR) { 771 stream_push_s8(&buf, s8("\x1B[<")); 772 stream_push_i64(&buf, value); 773 stream_push_byte(&buf, ';'); 774 stream_push_i64(&buf, pos.x + 1); 775 stream_push_byte(&buf, ';'); 776 stream_push_i64(&buf, pos.y + 1); 777 stream_push_byte(&buf, released? 'm' : 'M'); 778 } else if ((pos.x < (255 - 32)) && (pos.y < (255 - 32))) { 779 stream_push_s8(&buf, s8("\x1B[M")); 780 stream_push_byte(&buf, 32 + value); 781 stream_push_byte(&buf, 32 + pos.x + 1); 782 stream_push_byte(&buf, 32 + pos.y + 1); 783 } else { 784 INVALID_CODE_PATH; 785 return; 786 } 787 788 t->platform->write(t->child, stream_to_s8(&buf)); 789 } 790 791 static void 792 begin_terminal_interaction(Term *t, TerminalInput *input, u32 click_count) 793 { 794 if (t->mode.win & WM_MOUSE_MASK) { 795 report_mouse(t, input, 0, 1); 796 } else { 797 if (pressed_last_frame(input->keys + MOUSE_LEFT)) 798 begin_selection(t, click_count, input->mouse); 799 } 800 } 801 802 static b32 803 terminal_interaction(Term *t, PlatformAPI *platform, TerminalInput *input, u32 click_count) 804 { 805 806 b32 should_end_interaction = all_mouse_up(input); 807 if (t->mode.win & WM_MOUSE_MASK) { 808 if (t->mode.win & WM_MOUSE_TRK) 809 report_mouse(t, input, should_end_interaction, 0); 810 } else { 811 update_selection(t, input); 812 if (pressed_last_frame(input->keys + MOUSE_MIDDLE)) 813 paste(t, platform, (Arg){.i = CLIPBOARD_1}); 814 815 b32 shift_down = input->modifiers & MOD_SHIFT; 816 if (input->mouse_scroll.y) { 817 if (t->mode.term & TM_ALTSCREEN) { 818 iptr child = t->child; 819 if (input->mouse_scroll.y > 0) { 820 if (shift_down) platform->write(child, s8("\x1B[5;2~")); 821 else platform->write(child, s8("\x19")); 822 } else { 823 if (shift_down) platform->write(child, s8("\x1B[6;2~")); 824 else platform->write(child, s8("\x05")); 825 } 826 } else { 827 Arg a = {.i = (i32)input->mouse_scroll.y}; 828 if (shift_down) 829 a.i *= 5; 830 scroll(t, platform, a); 831 } 832 } 833 } 834 835 return should_end_interaction; 836 } 837 838 DEBUG_EXPORT VTGL_HANDLE_KEYS_FN(vtgl_handle_keys) 839 { 840 Term *t = memory->memory; 841 PlatformAPI *platform = &memory->platform_api; 842 iptr child = t->child; 843 844 #ifdef _DEBUG 845 if (key == KEY_F1 && action == ACT_PRESS) { 846 dump_lines_to_file(t); 847 return; 848 } 849 if (key == KEY_F11 && action == ACT_PRESS) { 850 /* TODO: probably move this into the debug frame start */ 851 DebugState *ds = memory->debug_memory; 852 ds->paused = !ds->paused; 853 return; 854 } 855 if (key == KEY_F12 && action == ACT_PRESS) { 856 t->gl.flags ^= DRAW_DEBUG_OVERLAY; 857 input->window_refreshed = 1; 858 return; 859 } 860 #endif 861 862 /* NOTE: handle mapped keybindings */ 863 u32 enc = ENCODE_KEY(action, modifiers, key); 864 for (u32 i = 0; i < ARRAY_COUNT(g_hotkeys); i++) { 865 struct hotkey *hk = g_hotkeys + i; 866 if (hk->key == enc) { 867 b32 handled = hk->fn(t, &memory->platform_api, hk->arg); 868 if (handled) 869 return; 870 } 871 } 872 873 /* NOTE: send control sequences */ 874 if (modifiers & MOD_CONTROL && action != ACT_RELEASE) { 875 /* TODO: this is wrong. look up where 8-bit modifiers should be sent */ 876 if (0 && t->mode.win & WM_8BIT) { 877 if (key < 0x7F) { 878 platform->write(child, utf8_encode(key | 0x80)); 879 return; 880 } 881 } else if (BETWEEN(key, 0x40, 0x5F)) { 882 platform->write(child, utf8_encode(key - 0x40)); 883 return; 884 } 885 } 886 887 /* TODO: construct a hash table of bound keys */ 888 switch (ENCODE_KEY(action, 0, key)) { 889 case ENCODE_KEY(ACT_PRESS, 0, KEY_ESCAPE): 890 case ENCODE_KEY(ACT_REPEAT, 0, KEY_ESCAPE): 891 platform->write(child, s8("\x1B")); 892 break; 893 case ENCODE_KEY(ACT_PRESS, 0, KEY_TAB): 894 case ENCODE_KEY(ACT_REPEAT, 0, KEY_TAB): 895 platform->write(child, s8("\t")); 896 break; 897 case ENCODE_KEY(ACT_PRESS, 0, KEY_ENTER): 898 case ENCODE_KEY(ACT_REPEAT, 0, KEY_ENTER): 899 platform->write(child, s8("\r")); 900 break; 901 case ENCODE_KEY(ACT_PRESS, 0, KEY_BACKSPACE): 902 case ENCODE_KEY(ACT_REPEAT, 0, KEY_BACKSPACE): 903 platform->write(child, s8("\x7F")); 904 break; 905 case ENCODE_KEY(ACT_PRESS, 0, KEY_UP): 906 case ENCODE_KEY(ACT_REPEAT, 0, KEY_UP): 907 if (t->mode.win & WM_APPCURSOR) 908 platform->write(child, s8("\x1BOA")); 909 else 910 platform->write(child, s8("\x1B[A")); 911 break; 912 case ENCODE_KEY(ACT_PRESS, 0, KEY_DOWN): 913 case ENCODE_KEY(ACT_REPEAT, 0, KEY_DOWN): 914 if (t->mode.win & WM_APPCURSOR) 915 platform->write(child, s8("\x1BOB")); 916 else 917 platform->write(child, s8("\x1B[B")); 918 break; 919 case ENCODE_KEY(ACT_PRESS, 0, KEY_RIGHT): 920 case ENCODE_KEY(ACT_REPEAT, 0, KEY_RIGHT): 921 if (t->mode.win & WM_APPCURSOR) 922 platform->write(child, s8("\x1BOC")); 923 else 924 platform->write(child, s8("\x1B[C")); 925 break; 926 case ENCODE_KEY(ACT_PRESS, 0, KEY_LEFT): 927 case ENCODE_KEY(ACT_REPEAT, 0, KEY_LEFT): 928 if (t->mode.win & WM_APPCURSOR) 929 platform->write(child, s8("\x1BOD")); 930 else 931 platform->write(child, s8("\x1B[D")); 932 break; 933 case ENCODE_KEY(ACT_PRESS, 0, KEY_PAGE_UP): 934 case ENCODE_KEY(ACT_REPEAT, 0, KEY_PAGE_UP): 935 if (modifiers & MOD_CONTROL) platform->write(child, s8("\x1B[5;5~")); 936 else if (modifiers & MOD_SHIFT) platform->write(child, s8("\x1B[5;2~")); 937 else platform->write(child, s8("\x1B[5~")); 938 break; 939 case ENCODE_KEY(ACT_PRESS, 0, KEY_PAGE_DOWN): 940 case ENCODE_KEY(ACT_REPEAT, 0, KEY_PAGE_DOWN): 941 if (modifiers & MOD_CONTROL) platform->write(child, s8("\x1B[6;5~")); 942 else if (modifiers & MOD_SHIFT) platform->write(child, s8("\x1B[6;2~")); 943 else platform->write(child, s8("\x1B[6~")); 944 break; 945 } 946 } 947 948 static b32 949 should_start_interaction(TerminalInput *input) 950 { 951 b32 result = input->mouse_scroll.y || input->mouse_scroll.x || 952 pressed_last_frame(input->keys + MOUSE_LEFT) || 953 pressed_last_frame(input->keys + MOUSE_MIDDLE) || 954 pressed_last_frame(input->keys + MOUSE_RIGHT); 955 return result; 956 } 957 958 static void 959 begin_interaction(Term *t, InteractionState *is, TerminalInput *input) 960 { 961 is->click_count++; 962 if (is->multi_click_t < 0) 963 is->multi_click_t = INTERACTION_MULTI_CLICK_TIME; 964 965 if (is->hot.type != IS_NONE) { 966 if (is->hot.type == IS_AUTO) { 967 //switch (is->hot.var.type) { 968 // /* TODO: start the interaction */ 969 //} 970 } 971 972 is->active = is->hot; 973 974 switch (is->active.type) { 975 case IS_TERM: 976 begin_terminal_interaction(t, input, is->click_count); 977 break; 978 default: 979 break; 980 } 981 } else { 982 is->active = (Interaction){.type = IS_NOP}; 983 } 984 } 985 986 static void 987 end_interaction(InteractionState *is, TerminalInput *input) 988 { 989 is->active = (Interaction){.type = IS_NONE}; 990 } 991 992 static void 993 handle_interactions(Term *t, TerminalInput *input, PlatformAPI *platform) 994 { 995 InteractionState *is = &t->interaction; 996 997 ButtonState *mouse_left = input->keys + MOUSE_LEFT; 998 999 is->multi_click_t -= dt_for_frame; 1000 if (!mouse_left->ended_down && is->multi_click_t < 0) { 1001 is->click_count = 0; 1002 } 1003 1004 if (is->hot.type != IS_NONE) { 1005 if (should_start_interaction(input)) { 1006 end_interaction(is, input); 1007 begin_interaction(t, is, input); 1008 } 1009 } 1010 1011 switch (is->active.type) { 1012 case IS_NONE: break; 1013 case IS_NOP: break; 1014 case IS_SET: end_interaction(is, input); break; 1015 case IS_AUTO: /* TODO */ break; 1016 case IS_DRAG: /* TODO */ break; 1017 case IS_DEBUG: /* TODO */ break; 1018 case IS_TERM: { 1019 if (terminal_interaction(t, platform, input, is->click_count)) 1020 end_interaction(is, input); 1021 } break; 1022 } 1023 } 1024 1025 static void 1026 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *data) 1027 { 1028 (void)src; (void)type; (void)id; 1029 Stream *err = (Stream *)data; 1030 stream_push_s8(err, s8("[GL Error ")); 1031 switch (lvl) { 1032 case GL_DEBUG_SEVERITY_HIGH: stream_push_s8(err, s8("HIGH]: ")); break; 1033 case GL_DEBUG_SEVERITY_MEDIUM: stream_push_s8(err, s8("MEDIUM]: ")); break; 1034 case GL_DEBUG_SEVERITY_LOW: stream_push_s8(err, s8("LOW]: ")); break; 1035 case GL_DEBUG_SEVERITY_NOTIFICATION: stream_push_s8(err, s8("NOTIFICATION]: ")); break; 1036 default: stream_push_s8(err, s8("INVALID]: ")); break; 1037 } 1038 stream_push_s8(err, (s8){.len = len, .data = (u8 *)msg}); 1039 stream_push_byte(err, '\n'); 1040 os_write_err_msg(stream_to_s8(err)); 1041 err->widx = 0; 1042 } 1043 1044 static u32 1045 gen_2D_texture(iv2 size, u32 format, u32 filter, u32 *rgba) 1046 { 1047 /* TODO: logging */ 1048 u32 result; 1049 glGenTextures(1, &result); 1050 glBindTexture(GL_TEXTURE_2D, result); 1051 glTexImage2D(GL_TEXTURE_2D, 0, format, size.w, size.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); 1052 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); 1053 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); 1054 return result; 1055 } 1056 1057 DEBUG_EXPORT VTGL_INITIALIZE_FN(vtgl_initialize) 1058 { 1059 Term *t = (Term *)memory->memory; 1060 Arena a = {.beg = (u8 *)(t + 1), .end = memory->memory + memory->memory_size}; 1061 1062 t->platform = &memory->platform_api; 1063 1064 t->cursor.state = CURSOR_NORMAL; 1065 cursor_reset(t); 1066 1067 memory->platform_api.allocate_ring_buffer(&t->views[0].log, BACKLOG_SIZE); 1068 line_buf_alloc(&t->views[0].lines, &a, t->views[0].log.buf, t->cursor.style, BACKLOG_LINES); 1069 memory->platform_api.allocate_ring_buffer(&t->views[1].log, ALT_BACKLOG_SIZE); 1070 line_buf_alloc(&t->views[1].lines, &a, t->views[1].log.buf, t->cursor.style, ALT_BACKLOG_LINES); 1071 1072 t->views[0].fb.backing_store = memory_block_from_arena(&a, MB(2)); 1073 t->views[1].fb.backing_store = memory_block_from_arena(&a, MB(2)); 1074 1075 t->gl.glyph_bitmap_dim = monitor_size; 1076 init_fonts(&t->fa, &a, t->gl.glyph_bitmap_dim); 1077 selection_clear(&t->selection); 1078 v2 cs = get_cell_size(&t->fa); 1079 iv2 requested_size = { 1080 .x = cs.x * requested_cells.x + 2 * g_term_margin.x, 1081 .y = cs.y * requested_cells.y + 2 * g_term_margin.y, 1082 }; 1083 if (requested_cells.x < 0 || requested_cells.y) 1084 requested_size = (iv2){.x = -1, .y = -1}; 1085 1086 t->size = (iv2){.x = 1, .y = 1}; 1087 initialize_framebuffer(&t->views[0].fb, t->size); 1088 initialize_framebuffer(&t->views[1].fb, t->size); 1089 1090 t->work_queue_items = alloc(&a, typeof(*t->work_queue_items), 1 << 6); 1091 t->work_queue_capacity = 1 << 6; 1092 1093 queue_shader_reload_ctx *reload_ctxs = alloc(&a, typeof(*reload_ctxs), SHADER_COUNT); 1094 1095 s8 shader_infos[SHADER_COUNT] = { 1096 [SHADER_POST] = s8("Post Processing Shader Reloaded!\n"), 1097 [SHADER_RECTS] = s8("UI Shader Reloaded!\n"), 1098 [SHADER_RENDER] = s8("Render Shader Reloaded!\n"), 1099 }; 1100 1101 for (u32 i = 0; i < SHADER_COUNT; i++) { 1102 Stream path = arena_stream(a); 1103 stream_push_s8(&path, g_shader_path_prefix); 1104 if (path.widx && path.buf[path.widx - 1] != memory->platform_api.path_separator) 1105 stream_push_byte(&path, memory->platform_api.path_separator); 1106 1107 queue_shader_reload_ctx *src = reload_ctxs + i; 1108 src->info = shader_infos[i]; 1109 src->stage = i; 1110 src->t = t; 1111 stream_push_s8(&path, fs_name[i]); 1112 stream_push_byte(&path, 0); 1113 memory->platform_api.add_file_watch(path.buf, queue_shader_reload, src); 1114 a.beg = path.buf + path.widx; 1115 } 1116 1117 t->error_stream = stream_alloc(&a, KB(256)); 1118 t->saved_title = stream_alloc(&a, KB(16)); 1119 t->arena_for_frame = a; 1120 t->child = child; 1121 1122 /* NOTE: Set up OpenGL Render Pipeline */ 1123 glDebugMessageCallback(gl_debug_logger, &t->error_stream); 1124 glEnable(GL_DEBUG_OUTPUT); 1125 /* NOTE: shut up useless shader compilation statistics */ 1126 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, 1127 GL_DEBUG_SEVERITY_NOTIFICATION, 1128 0, 0, GL_FALSE); 1129 1130 glGenVertexArrays(1, &t->gl.vao); 1131 glBindVertexArray(t->gl.vao); 1132 1133 glGenBuffers(ARRAY_COUNT(t->gl.vbos), t->gl.vbos); 1134 1135 RenderPushBuffer *rpb = NULL; 1136 /* NOTE: vertex position buffer */ 1137 glBindBuffer(GL_ARRAY_BUFFER, t->gl.vbos[0]); 1138 glBufferData(GL_ARRAY_BUFFER, sizeof(rpb->positions), 0, GL_DYNAMIC_DRAW); 1139 glEnableVertexAttribArray(0); 1140 glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, 0); 1141 1142 /* NOTE: vertex texture coordinate buffer */ 1143 glBindBuffer(GL_ARRAY_BUFFER, t->gl.vbos[1]); 1144 glBufferData(GL_ARRAY_BUFFER, sizeof(rpb->texture_coordinates), 0, GL_DYNAMIC_DRAW); 1145 glEnableVertexAttribArray(1); 1146 glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); 1147 1148 /* NOTE: vertex colour buffer */ 1149 glBindBuffer(GL_ARRAY_BUFFER, t->gl.vbos[2]); 1150 glBufferData(GL_ARRAY_BUFFER, sizeof(rpb->colours), 0, GL_DYNAMIC_DRAW); 1151 glEnableVertexAttribArray(2); 1152 glVertexAttribPointer(2, 4, GL_FLOAT, 0, 0, 0); 1153 1154 /* NOTE: fill in element index buffer */ 1155 i32 *element_indices = alloc(&a, i32, 6 * ARRAY_COUNT(rpb->positions)); 1156 for (i32 i = 0, j = 0; i < 6 * ARRAY_COUNT(rpb->positions); i += 6, j++) { 1157 element_indices[i + 0] = 4 * j; 1158 element_indices[i + 1] = 4 * j + 1; 1159 element_indices[i + 2] = 4 * j + 2; 1160 element_indices[i + 3] = 4 * j; 1161 element_indices[i + 4] = 4 * j + 2; 1162 element_indices[i + 5] = 4 * j + 3; 1163 } 1164 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, t->gl.vbos[4]); 1165 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * ARRAY_COUNT(rpb->positions) * sizeof(i32), 1166 element_indices, GL_STATIC_DRAW); 1167 1168 t->gl.glyph_bitmap_tex = gen_2D_texture(t->gl.glyph_bitmap_dim, GL_RGBA, GL_NEAREST, 0); 1169 /* NOTE: set pixel 0,0 to white (tile 0,0 is reserved). We can use this texture for 1170 * drawing glyphs from the font cache or for drawing plain rectangles */ 1171 u32 white = 0xFFFFFFFF; 1172 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &white); 1173 LABEL_GL_OBJECT(GL_TEXTURE, t->gl.glyph_bitmap_tex, s8("Glyph_Bitmap")); 1174 1175 glEnable(GL_BLEND); 1176 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1177 1178 glDisable(GL_DEPTH_TEST); 1179 glDisable(GL_CULL_FACE); 1180 1181 /* NOTE: Generate an intermediate framebuffer for rendering to. This 1182 * allows for additional post processing via a second shader stage */ 1183 glGenFramebuffers(1, &t->gl.fb); 1184 glBindFramebuffer(GL_FRAMEBUFFER, t->gl.fb); 1185 1186 t->gl.fb_tex_unit = 1; 1187 glActiveTexture(GL_TEXTURE0 + t->gl.fb_tex_unit); 1188 iv2 ws = monitor_size; 1189 t->gl.fb_tex = gen_2D_texture(ws, GL_RGBA, GL_NEAREST, 0); 1190 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, t->gl.fb_tex, 0); 1191 LABEL_GL_OBJECT(GL_TEXTURE, t->gl.fb_tex, s8("Framebuffer_Texture")); 1192 1193 glGenBuffers(1, &t->gl.render_shader_ubo); 1194 glBindBuffer(GL_UNIFORM_BUFFER, t->gl.render_shader_ubo); 1195 glBufferData(GL_UNIFORM_BUFFER, sizeof(ShaderParameters), 0, GL_DYNAMIC_DRAW); 1196 glBindBufferBase(GL_UNIFORM_BUFFER, 0, t->gl.render_shader_ubo); 1197 LABEL_GL_OBJECT(GL_BUFFER, t->gl.render_shader_ubo, s8("ShaderParameters")); 1198 1199 glActiveTexture(GL_TEXTURE0); 1200 1201 reload_all_shaders(&t->gl, &memory->platform_api, a); 1202 1203 return requested_size; 1204 } 1205 1206 DEBUG_EXPORT VTGL_ACTIVE_SELECTION_FN(vtgl_active_selection) 1207 { 1208 Term *t = memory->memory; 1209 Range result = t->selection.range; 1210 if (out) stream_push_selection(out, t->views[t->view_idx].fb.rows, result, t->size.w); 1211 return result; 1212 } 1213 1214 DEBUG_EXPORT VTGL_RENDER_FRAME_FN(vtgl_render_frame) 1215 { 1216 BEGIN_TIMED_BLOCK(); 1217 1218 Term *t = memory->memory; 1219 1220 dt_for_frame = input->dt; 1221 1222 TempArena temp_arena = begin_temp_arena(&arena); 1223 1224 i32 queue_item; 1225 while ((queue_item = work_queue_pop(&t->work_queue, t->work_queue_capacity)) != -1) { 1226 work_queue_pop_commit(&t->work_queue); 1227 work_queue_entry *entry = t->work_queue_items + queue_item; 1228 switch (entry->type) { 1229 case WQ_RELOAD_SHADER: { 1230 queue_shader_reload_ctx *ctx = entry->ctx; 1231 reload_shader(&t->gl, &memory->platform_api, ctx->path, ctx->stage, 1232 ctx->info, arena); 1233 } break; 1234 case WQ_RELOAD_ALL_SHADERS: { 1235 reload_all_shaders(&t->gl, &memory->platform_api, arena); 1236 } break; 1237 default: INVALID_CODE_PATH; 1238 } 1239 } 1240 1241 /* NOTE: default state which can be overwritten later in the frame */ 1242 /* TODO: if (!t->ui_active) */ 1243 { 1244 t->interaction.hot.type = IS_TERM; 1245 } 1246 1247 glBindTexture(GL_TEXTURE_2D, t->gl.glyph_bitmap_tex); 1248 1249 BEGIN_NAMED_BLOCK(update_render); 1250 1251 RenderCtx rc = make_render_ctx(&arena, &t->gl, &t->fa); 1252 1253 glUseProgram(t->gl.programs[SHADER_RENDER]); 1254 glBindFramebuffer(GL_FRAMEBUFFER, t->gl.fb); 1255 clear_colour(); 1256 1257 /* NOTE(rnp): spin lock here so that we don't have to deal with rescheduling 1258 * a redraw. Remember that this failing is already unlikely. */ 1259 while (atomic_exchange_n(&t->resize_lock, 1) != 0); 1260 1261 if (t->gl.flags & RESIZE_RENDERER) 1262 resize(t, &memory->platform_api, input->window_size); 1263 1264 if (t->gl.queued_render) { 1265 t->gl.queued_render = 0; 1266 u32 cell_count = t->size.h * t->size.w; 1267 RenderCell *render_buf = alloc(&arena, RenderCell, cell_count); 1268 render_framebuffer(t, render_buf, input, arena); 1269 glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, 1270 cell_count * sizeof(*render_buf), render_buf); 1271 } 1272 render_cursor(t, input->window_focused, arena); 1273 1274 t->resize_lock = 0; 1275 1276 ShaderParameters *sp = &t->gl.shader_parameters; 1277 sp->blink_parameter += 2 * PI * g_blink_speed * dt_for_frame; 1278 if (sp->blink_parameter > 2 * PI) sp->blink_parameter -= 2 * PI; 1279 sp->reverse_video_mask = REVERSE_VIDEO_MASK * !!(t->mode.win & WM_REVERSE); 1280 1281 glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(*sp), sp); 1282 1283 push_rect_textured(&rc, (Rect){.size = v2_from_iv2(t->gl.window_size)}, (v4){0}, 0); 1284 flush_render_push_buffer(&rc); 1285 1286 static f32 param = 0; 1287 static f32 p_scale = 1; 1288 param += p_scale * 0.005 * dt_for_frame; 1289 if (param > 1.0f || param < 0) 1290 p_scale *= -1.0f; 1291 1292 glBindFramebuffer(GL_FRAMEBUFFER, 0); 1293 1294 clear_colour(); 1295 glUseProgram(t->gl.programs[SHADER_POST]); 1296 glUniform1i(t->gl.post.texslot, t->gl.fb_tex_unit); 1297 glUniform1f(t->gl.post.param, param); 1298 1299 push_rect_textured(&rc, (Rect){.size = v2_from_iv2(t->gl.window_size)}, (v4){0}, 1); 1300 flush_render_push_buffer(&rc); 1301 END_NAMED_BLOCK(update_render); 1302 1303 /* NOTE: this happens at the end so that ui stuff doesn't go through the post 1304 * processing/effects shader */ 1305 glUseProgram(t->gl.programs[SHADER_RECTS]); 1306 1307 BEGIN_NAMED_BLOCK(debug_overlay); 1308 draw_debug_overlay(memory, input, &rc); 1309 END_NAMED_BLOCK(debug_overlay); 1310 1311 end_temp_arena(temp_arena); 1312 1313 END_TIMED_BLOCK(); 1314 } 1315 1316 DEBUG_EXPORT VTGL_FRAME_STEP_FN(vtgl_frame_step) 1317 { 1318 BEGIN_NAMED_BLOCK(debug_end_frame); 1319 debug_frame_end(memory, input); 1320 END_NAMED_BLOCK(debug_end_frame); 1321 1322 BEGIN_TIMED_BLOCK(); 1323 1324 Term *t = memory->memory; 1325 1326 dt_for_frame = input->dt; 1327 1328 t->temp_arena = begin_temp_arena(&t->arena_for_frame); 1329 1330 if (t->gl.flags & NEEDS_RESIZE || !equal_iv2(input->window_size, t->gl.window_size)) { 1331 /* NOTE(rnp): we skip the resize this time through so that we don't add 1332 * input latency waiting for the render thread to release this lock */ 1333 if (atomic_exchange_n(&t->resize_lock, 1) == 0) { 1334 resize_terminal(t, &memory->platform_api, input->window_size); 1335 t->resize_lock = 0; 1336 } else { 1337 input->pending_updates = 1; 1338 } 1339 } 1340 1341 if (input->executable_reloaded) { 1342 work_queue_insert(t, WQ_RELOAD_ALL_SHADERS, 0); 1343 } 1344 1345 BEGIN_NAMED_BLOCK(mouse_and_keyboard_input); 1346 if (input->character_input.len) { 1347 if (t->scroll_offset) { 1348 t->scroll_offset = 0; 1349 t->gl.flags |= NEEDS_REFILL; 1350 } 1351 memory->platform_api.write(t->child, input->character_input); 1352 } 1353 1354 handle_interactions(t, input, &memory->platform_api); 1355 END_NAMED_BLOCK(mouse_and_keyboard_input); 1356 1357 if (t->gl.flags & NEEDS_REFILL) { 1358 blit_lines(t, t->arena_for_frame); 1359 t->gl.queued_render = 1; 1360 } 1361 1362 BEGIN_NAMED_BLOCK(input_from_child); 1363 if (input->data_available) { 1364 RingBuf *rb = &t->views[t->view_idx].log; 1365 s8 buffer = {.len = rb->cap - t->unprocessed_bytes, .data = rb->buf + rb->widx}; 1366 1367 size bytes_read = memory->platform_api.read(t->child, buffer); 1368 ASSERT(bytes_read <= rb->cap); 1369 commit_to_rb(t->views + t->view_idx, bytes_read); 1370 1371 t->unprocessed_bytes += bytes_read; 1372 s8 raw = { 1373 .len = t->unprocessed_bytes, 1374 .data = rb->buf + (rb->widx - t->unprocessed_bytes) 1375 }; 1376 handle_input(t, t->arena_for_frame, raw); 1377 t->gl.queued_render = 1; 1378 } 1379 END_NAMED_BLOCK(input_from_child); 1380 1381 end_temp_arena(t->temp_arena); 1382 1383 END_TIMED_BLOCK(); 1384 1385 return t->gl.queued_render || input->window_refreshed || t->gl.flags & DRAW_DEBUG_OVERLAY 1386 || !work_queue_empty(&t->work_queue, t->work_queue_capacity); 1387 } 1388 1389 #ifdef _DEBUG 1390 #include "debug.c" 1391 #endif