vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

util.c (12858B)


      1 /* See LICENSE for copyright details */
      2 
      3 /* NOTE: avoids braindead standards committee UB when n is negative or shift is > 31 */
      4 static u32
      5 safe_left_shift(u32 n, u32 shift)
      6 {
      7 	u64 result = (u64)n << (shift & 63);
      8 	return result & 0xFFFFFFFF;
      9 }
     10 
     11 static u32
     12 round_down_power_of_2(u32 a)
     13 {
     14 	u32 result = 0x80000000UL >> clz_u32(a);
     15 	return result;
     16 }
     17 
     18 static v2
     19 sub_v2(v2 a, v2 b)
     20 {
     21 	v2 result;
     22 	result.x = a.x - b.x;
     23 	result.y = a.y - b.y;
     24 	return result;
     25 }
     26 
     27 static f32
     28 length_v2(v2 a)
     29 {
     30 	f32 result = a.x * a.x + a.y * a.y;
     31 	return result;
     32 }
     33 
     34 static b32
     35 equal_iv2(iv2 a, iv2 b)
     36 {
     37 	b32 result = a.x == b.x && a.y == b.y;
     38 	return result;
     39 }
     40 
     41 static b32
     42 equal_uv2(uv2 a, uv2 b)
     43 {
     44 	b32 result = a.x == b.x && a.y == b.y;
     45 	return result;
     46 }
     47 
     48 static v2
     49 v2_from_iv2(iv2 a)
     50 {
     51 	v2 result = {.x = a.x, .y = a.y};
     52 	return result;
     53 }
     54 
     55 static b32
     56 is_valid_range(Range r)
     57 {
     58 	b32 result = !equal_iv2(r.end, INVALID_RANGE_END);
     59 	return result;
     60 }
     61 
     62 static b32
     63 equal_range(Range a, Range b)
     64 {
     65 	b32 result = equal_iv2(a.start, b.start) && equal_iv2(a.end, b.end);
     66 	return result;
     67 }
     68 
     69 static b32
     70 point_in_rect(v2 point, Rect rect)
     71 {
     72 	v2 max = {.x = rect.pos.x + rect.size.w, .y = rect.pos.y + rect.size.h};
     73 	b32 result = BETWEEN(point.x, rect.pos.x, max.x) && BETWEEN(point.y, rect.pos.y, max.y);
     74 	return result;
     75 }
     76 
     77 static Range
     78 normalize_range(Range r)
     79 {
     80 	Range result;
     81 	if (!is_valid_range(r) || r.start.y < r.end.y) {
     82 		result = r;
     83 	} else if (r.end.y < r.start.y) {
     84 		result = (Range){.start = r.end, .end = r.start};
     85 	} else {
     86 		result.start.y = result.end.y = r.start.y;
     87 		result.start.x = MIN(r.start.x, r.end.x);
     88 		result.end.x   = MAX(r.start.x, r.end.x);
     89 	}
     90 	return result;
     91 }
     92 
     93 /* NOTE(rnp): based on nullprogram's lock-free, concurrent,
     94  * generic queue in 32 bits */
     95 static i32
     96 work_queue_push(u32 *q, u32 capacity)
     97 {
     98 	ASSERT(ISPOWEROFTWO(capacity));
     99 	u32 r    = atomic_load(q);
    100 	i32 mask = capacity - 1;
    101 	i32 head =  r         & mask;
    102 	i32 tail = (r >> 16)  & mask;
    103 	i32 next = (head + 1) & mask;
    104 	/* NOTE(rnp): prevent an overflow into the tail on commit */
    105 	if (r & 0x8000) atomic_and(q, ~0x8000u);
    106 	return next == tail ? -1 : head;
    107 }
    108 
    109 static void
    110 work_queue_push_commit(u32 *q)
    111 {
    112 	atomic_fetch_add(q, 1);
    113 }
    114 
    115 static i32
    116 work_queue_pop(u32 *q, u32 capacity)
    117 {
    118 	ASSERT(ISPOWEROFTWO(capacity));
    119 	u32 r    = atomic_load(q);
    120 	i32 mask = capacity - 1;
    121 	i32 head =  r        & mask;
    122 	i32 tail = (r >> 16) & mask;
    123 	return head == tail ? -1 : tail;
    124 }
    125 
    126 static void
    127 work_queue_pop_commit(u32 *q)
    128 {
    129 	atomic_fetch_add(q, 0x10000u);
    130 }
    131 
    132 static b32
    133 work_queue_empty(u32 *q, u32 capacity)
    134 {
    135 	ASSERT(ISPOWEROFTWO(capacity));
    136 	u32 r    = atomic_load(q);
    137 	i32 mask = capacity - 1;
    138 	i32 head =  r        & mask;
    139 	i32 tail = (r >> 16) & mask;
    140 	return head == tail;
    141 }
    142 
    143 static void
    144 work_queue_insert(Term *t, u32 type, void *ctx)
    145 {
    146 	i32 index = work_queue_push(&t->work_queue, t->work_queue_capacity);
    147 	/* NOTE(rnp): if we ever fill this up we need to resize the queue */
    148 	ASSERT(index != -1);
    149 	work_queue_push_commit(&t->work_queue);
    150 	t->work_queue_items[index].type = type;
    151 	t->work_queue_items[index].ctx  = ctx;
    152 }
    153 
    154 static void
    155 mem_copy(void *restrict src, void *restrict dest, size len)
    156 {
    157 	ASSERT(len >= 0);
    158 	u8 *s = src, *d = dest;
    159 	for (; len; len--) *d++ = *s++;
    160 }
    161 
    162 #define zero_struct(s) mem_clear(s, 0, sizeof(typeof(*s)))
    163 static void *
    164 mem_clear(void *p_, u8 c, size len)
    165 {
    166 	u8 *p = p_;
    167 	while (len) p[--len] = c;
    168 	return p;
    169 }
    170 
    171 #define push_struct(a, t) alloc(a, t, 1)
    172 #define alloc(a, t, n)  (t *)alloc_(a, sizeof(t), _Alignof(t), n)
    173 static void *
    174 alloc_(Arena *a, size len, size align, size count)
    175 {
    176 	size padding   = -(uintptr_t)a->beg & (align - 1);
    177 	size available = a->end - a->beg - padding;
    178 	if (available <= 0 || available / len < count) {
    179 		ASSERT(0);
    180 	}
    181 
    182 	void *p  = a->beg + padding;
    183 	a->beg  += padding + count * len;
    184 	return mem_clear(p, 0, count * len);
    185 }
    186 
    187 static Arena
    188 arena_from_memory_block(MemoryBlock memory)
    189 {
    190 	Arena result;
    191 	result.beg = memory.memory;
    192 	result.end = memory.memory + memory.size;
    193 	return result;
    194 }
    195 
    196 static MemoryBlock
    197 memory_block_from_arena(Arena *a, size requested_size)
    198 {
    199 	MemoryBlock result;
    200 	result.memory = alloc_(a, requested_size, 64, 1);
    201 	result.size   = requested_size;
    202 	return result;
    203 }
    204 
    205 static Arena
    206 sub_arena(Arena *a, size size)
    207 {
    208 	Arena result = {0};
    209 	result.beg   = alloc_(a, size, 64, 1);
    210 	result.end   = result.beg + size;
    211 	return result;
    212 }
    213 
    214 static TempArena
    215 begin_temp_arena(Arena *a)
    216 {
    217 	TempArena result;
    218 	result.arena   = a;
    219 	result.old_beg = a->beg;
    220 	return result;
    221 }
    222 
    223 static void
    224 end_temp_arena(TempArena ta)
    225 {
    226 	Arena *a = ta.arena;
    227 	ASSERT(a->beg >= ta.old_beg);
    228 	a->beg = ta.old_beg;
    229 }
    230 
    231 /* NOTE: This performs wrapping of the ring buffer as needed; since a line could be in
    232  * progress this must also adjust the start and end of the current line */
    233 static void
    234 commit_to_rb(TermView *tv, size len)
    235 {
    236 	ASSERT(ABS(len) <= tv->log.cap);
    237 
    238 	tv->log.widx   += len;
    239 	tv->log.filled += len;
    240 
    241 	CLAMP(tv->log.filled, 0, tv->log.cap);
    242 	if (tv->log.widx >= tv->log.cap) {
    243 		tv->log.widx -= tv->log.cap;
    244 
    245 		size line = tv->lines.widx;
    246 		tv->lines.buf[line].start -= tv->log.cap;
    247 		tv->lines.buf[line].end   -= tv->log.cap;
    248 	}
    249 
    250 	ASSERT(tv->log.filled >= 0);
    251 	ASSERT(tv->log.widx >= 0 && tv->log.widx < tv->log.cap);
    252 }
    253 
    254 static void
    255 line_buf_alloc(LineBuf *lb, Arena *a, u8 *start_position, CellStyle state, size capacity)
    256 {
    257 	lb->cap          = capacity;
    258 	lb->filled       = 0;
    259 	lb->widx         = 0;
    260 	lb->buf          = alloc(a, Line, capacity);
    261 	lb->buf[0].start = start_position;
    262 	lb->buf[0].end   = start_position;
    263 	lb->buf[0].cursor_state = state;
    264 }
    265 
    266 static s8
    267 s8alloc(Arena *a, size len)
    268 {
    269 	return (s8){.len = len, .data = alloc(a, u8, len)};
    270 }
    271 
    272 static s8
    273 c_str_to_s8(char *s)
    274 {
    275 	s8 result = {.data = (u8 *)s};
    276 	while (*s) { result.len++; s++; }
    277 	return result;
    278 }
    279 
    280 static b32
    281 s8_equal(s8 a, s8 b)
    282 {
    283 	b32 result = a.len == b.len;
    284 	for (size i = 0; result && i < a.len; i++)
    285 		result = a.data[i] == b.data[i];
    286 	return result;
    287 }
    288 
    289 static b32
    290 s8_prefix_of(s8 s, s8 match)
    291 {
    292 	b32 result = 0;
    293 	if (s.len <= match.len) {
    294 		s8 head = {.data = match.data, .len = s.len};
    295 		result  = s8_equal(s, head);
    296 	}
    297 	return result;
    298 }
    299 
    300 static s8
    301 s8_chop_at(s8 raw, u8 delim)
    302 {
    303 	size i;
    304 	for (i = 0; i < raw.len; i++) {
    305 		if (raw.data[i] == delim)
    306 			break;
    307 	}
    308 	s8 result = {.len = i, .data = raw.data};
    309 	return result;
    310 }
    311 
    312 static void
    313 s8_parse_i32_accum(struct conversion_result *result, s8 raw)
    314 {
    315 	result->status = CR_SUCCESS;
    316 
    317 	size i    = 0;
    318 	i32 scale = 1;
    319 	if (raw.len && raw.data[0] == '-') {
    320 		scale = -1;
    321 		i     = 1;
    322 	}
    323 
    324 	for (; i < raw.len; i++) {
    325 		i32 digit = (i32)raw.data[i] - '0';
    326 		if (BETWEEN(digit, 0, 9)) {
    327 			if (result->i > (I32_MAX - digit) / 10) {
    328 				result->status = CR_OUT_OF_RANGE;
    329 				result->i      = I32_MAX;
    330 			} else {
    331 				result->i = 10 * result->i + digit;
    332 			}
    333 		} else {
    334 			break;
    335 		}
    336 	}
    337 
    338 	if (i == 0 || (i == 1 && raw.data[0] == '-'))
    339 		result->status = CR_FAILURE;
    340 
    341 	result->unparsed  = (s8){.len = raw.len - i, .data = raw.data + i};
    342 	result->i        *= scale;
    343 }
    344 
    345 static struct conversion_result
    346 s8_parse_i32(s8 raw)
    347 {
    348 	struct conversion_result result = {0};
    349 	s8_parse_i32_accum(&result, raw);
    350 	return result;
    351 }
    352 
    353 static struct conversion_result
    354 s8_parse_i32_until(s8 raw, u8 delim)
    355 {
    356 	s8 chopped = s8_chop_at(raw, delim);
    357 	struct conversion_result result = s8_parse_i32(chopped);
    358 	result.unparsed = (s8){.data = raw.data + chopped.len, .len = raw.len - chopped.len};
    359 	return result;
    360 }
    361 
    362 static Stream
    363 arena_stream(Arena a)
    364 {
    365 	Stream result = {0};
    366 	result.cap    = a.end - a.beg;
    367 	result.buf    = (typeof(result.buf))a.beg;
    368 	return result;
    369 }
    370 
    371 static Stream
    372 stream_alloc(Arena *a, u32 cap)
    373 {
    374 	Stream result = {0};
    375 	result.cap    = cap;
    376 	result.buf    = alloc(a, typeof(*result.buf), cap);
    377 	return result;
    378 }
    379 
    380 static s8
    381 stream_to_s8(Stream *s)
    382 {
    383 	s8 result = {.len = s->widx, .data = s->buf};
    384 	return result;
    385 }
    386 
    387 static void
    388 stream_push_byte(Stream *s, u8 cp)
    389 {
    390 	s->errors |= !(s->cap - s->widx);
    391 	if (!s->errors)
    392 		s->buf[s->widx++] = cp;
    393 }
    394 
    395 static void
    396 stream_push_s8(Stream *s, s8 str)
    397 {
    398 	s->errors |= (s->cap - s->widx) < str.len;
    399 	if (!s->errors) {
    400 		for (size i = 0; i < str.len; i++)
    401 			s->buf[s->widx++] = str.data[i];
    402 	}
    403 }
    404 
    405 /* NOTE: how can he get away with not using a library for this */
    406 static void
    407 stream_push_s8_left_padded(Stream *s, s8 str, u32 width)
    408 {
    409 	for (u32 i = str.len; i < width; i++)
    410 		stream_push_byte(s, ' ');
    411 	stream_push_s8(s, str);
    412 }
    413 
    414 static void
    415 stream_push_s8s(Stream *s, u32 count, s8 *strs)
    416 {
    417 	while (count) { stream_push_s8(s, *strs++); count--; }
    418 }
    419 
    420 static void
    421 stream_push_hex_u64(Stream *s, u64 n)
    422 {
    423 	if (!s->errors) {
    424 		static u8 hex[16] = {"0123456789abcdef"};
    425 		u8  buf[16];
    426 		u8 *end = buf + sizeof(buf);
    427 		u8 *beg = end;
    428 		while (n) {
    429 			*--beg = hex[n & 0x0F];
    430 			n >>= 4;
    431 		}
    432 		while (end - beg < 2)
    433 			*--beg = '0';
    434 		stream_push_s8(s, (s8){.len = end - beg, .data = beg});
    435 	}
    436 }
    437 
    438 static void
    439 stream_push_u64_padded(Stream *s, u64 n, u32 width)
    440 {
    441 	if (!s->errors) {
    442 		u8 tmp[64];
    443 		u8 *end = tmp + sizeof(tmp);
    444 		u8 *beg = end;
    445 		do { *--beg = '0' + (n % 10); } while (n /= 10);
    446 
    447 		s8 str = {.len = end - beg, .data = beg};
    448 		for (u32 i = str.len; i < width; i++)
    449 			stream_push_byte(s, ' ');
    450 		stream_push_s8(s, str);
    451 	}
    452 }
    453 
    454 static void
    455 stream_push_u64(Stream *s, u64 n)
    456 {
    457 	stream_push_u64_padded(s, n, 0);
    458 }
    459 
    460 static void
    461 stream_push_i64(Stream *s, i64 n)
    462 {
    463 	s->errors |= n == I64_MIN;
    464 	if (!s->errors) {
    465 		if (n < 0) {
    466 			stream_push_byte(s, '-');
    467 			n = -n;
    468 		}
    469 		stream_push_u64_padded(s, n, 0);
    470 	}
    471 }
    472 
    473 static void
    474 stream_push_iv2(Stream *s, iv2 v)
    475 {
    476 	stream_push_byte(s, '{');
    477 	stream_push_i64(s, v.x);
    478 	stream_push_byte(s, ',');
    479 	stream_push_i64(s, v.y);
    480 	stream_push_byte(s, '}');
    481 }
    482 
    483 static void
    484 stream_push_f64(Stream *s, f64 f, i64 prec)
    485 {
    486 	if (f < 0) {
    487 		stream_push_byte(s, '-');
    488 		f *= -1;
    489 	}
    490 
    491 	/* NOTE: round last digit */
    492 	f += 0.5f / prec;
    493 
    494 	if (f >= (f64)(-1UL >> 1)) {
    495 		stream_push_s8(s, s8("inf"));
    496 	} else {
    497 		u64 integral = f;
    498 		u64 fraction = (f - integral) * prec;
    499 		stream_push_u64(s, integral);
    500 		stream_push_byte(s, '.');
    501 		for (u64 i = prec / 10; i > 1; i /= 10) {
    502 			if (i > fraction)
    503 				stream_push_byte(s, '0');
    504 		}
    505 		stream_push_u64(s, fraction);
    506 	}
    507 }
    508 
    509 static SelectionIterator
    510 selection_iterator(Range s, Row *rows, u32 term_width)
    511 {
    512 	SelectionIterator result;
    513 	result.rows       = rows;
    514 	result.cursor     = (iv2){.x = -1, .y = -1};
    515 	result.next       = s.start;
    516 	result.end        = s.end;
    517 	result.term_width = term_width;
    518 	return result;
    519 }
    520 
    521 static Cell *
    522 selection_next(SelectionIterator *s)
    523 {
    524 	Cell *result = 0;
    525 
    526 	if (!equal_iv2(s->cursor, s->end)) {
    527 		s->cursor = s->next;
    528 		result    = s->rows[s->next.y] + s->next.x++;
    529 		if (s->next.x == s->term_width) {
    530 			s->next.x = 0;
    531 			s->next.y++;
    532 		}
    533 	}
    534 
    535 	return result;
    536 }
    537 
    538 static s8
    539 envp_lookup(s8 name, char **e)
    540 {
    541 	ASSERT(name.data[name.len - 1] == '=');
    542 
    543 	s8 s, result = {0};
    544 	for (; *e; e++) {
    545 		s = c_str_to_s8(*e);
    546 		if (s8_prefix_of(name, s))
    547 			break;
    548 	}
    549 
    550 	if (*e) {
    551 		result = s;
    552 		result.len  -= name.len;
    553 		result.data += name.len;
    554 	}
    555 
    556 	return result;
    557 }
    558 
    559 static c8 **
    560 construct_c_str_array(Arena *a, SLLVariableVector vars)
    561 {
    562 	c8 **result = alloc(a, c8 *, vars.count + 1);
    563 
    564 	VariableLink *chain = vars.next;
    565 	for (u32 i = 0; i < vars.count; i++) {
    566 		ASSERT(chain->var->type == VT_S8);
    567 		result[i] = (c8 *)chain->var->s8.data;
    568 		chain     = chain->next;
    569 	}
    570 
    571 	return result;
    572 }
    573 
    574 static b32
    575 any_mouse_down(TerminalInput *input)
    576 {
    577 	b32 result = input->keys[MOUSE_LEFT].ended_down   ||
    578 	             input->keys[MOUSE_RIGHT].ended_down  ||
    579 	             input->keys[MOUSE_MIDDLE].ended_down;
    580 	return result;
    581 }
    582 
    583 static b32
    584 all_mouse_up(TerminalInput *input)
    585 {
    586 	b32 result = !input->keys[MOUSE_LEFT].ended_down   &&
    587 	             !input->keys[MOUSE_RIGHT].ended_down  &&
    588 	             !input->keys[MOUSE_MIDDLE].ended_down;
    589 	return result;
    590 }
    591 
    592 static void
    593 button_action(ButtonState *button, b32 pressed)
    594 {
    595 	if (pressed != button->ended_down)
    596 		button->transitions++;
    597 	button->ended_down = pressed;
    598 }
    599 
    600 static b32
    601 pressed_last_frame(ButtonState *button)
    602 {
    603 	b32 result = (button->transitions > 1) || (button->ended_down && button->transitions == 1);
    604 	return result;
    605 }
    606 
    607 static s8
    608 utf8_encode(u32 cp)
    609 {
    610 	static u8 buf[4];
    611 	s8 result = {.data = buf};
    612 	if (cp <= 0x7F) {
    613 		result.len = 1;
    614 		buf[0] = cp & 0x7F;
    615 	} else if (cp <= 0x7FF) {
    616 		result.len = 2;
    617 		buf[0] = ((cp >>  6) & 0x1F) | 0xC0;
    618 		buf[1] = ((cp >>  0) & 0x3F) | 0x80;
    619 	} else if (cp <= 0xFFFF) {
    620 		result.len = 3;
    621 		buf[0] = ((cp >> 12) & 0x0F) | 0xE0;
    622 		buf[1] = ((cp >>  6) & 0x3F) | 0x80;
    623 		buf[2] = ((cp >>  0) & 0x3F) | 0x80;
    624 	} else if (cp <= 0x10FFFF) {
    625 		result.len = 4;
    626 		buf[0] = ((cp >> 18) & 0x07) | 0xF0;
    627 		buf[1] = ((cp >> 12) & 0x3F) | 0x80;
    628 		buf[2] = ((cp >>  6) & 0x3F) | 0x80;
    629 		buf[3] = ((cp >>  0) & 0x3F) | 0x80;
    630 	} else {
    631 		buf[0] = '?';
    632 		result.len = 1;
    633 	}
    634 	return result;
    635 }
    636 
    637 #include "extern/utf8_decode.c"