vtgl

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

util.c (11978B)


      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 >> _lzcnt_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(*s0)))
    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 struct conversion_result
    301 i32_from_cstr(char *s, char delim)
    302 {
    303 	struct conversion_result ret = {.status = CR_FAILURE};
    304 	i32 scale = 1;
    305 
    306 	if (!s || !s[0])
    307 		return ret;
    308 
    309 	if (s[0] == '-') {
    310 		s++;
    311 		scale = -1;
    312 	}
    313 
    314 	for (; *s && *s != delim; s++) {
    315 		if (!BETWEEN(s[0], '0', '9'))
    316 			return ret;
    317 		ret.i *= 10;
    318 		ret.i += s[0] - '0';
    319 	}
    320 
    321 	ret.i        *= scale;
    322 	ret.status    = CR_SUCCESS;
    323 	ret.unparsed  = (*s == delim) ? s + 1 : s;
    324 
    325 	return ret;
    326 }
    327 
    328 static Stream
    329 arena_stream(Arena a)
    330 {
    331 	Stream result = {0};
    332 	result.cap    = a.end - a.beg;
    333 	result.buf    = (typeof(result.buf))a.beg;
    334 	return result;
    335 }
    336 
    337 static Stream
    338 stream_alloc(Arena *a, u32 cap)
    339 {
    340 	Stream result = {0};
    341 	result.cap    = cap;
    342 	result.buf    = alloc(a, typeof(*result.buf), cap);
    343 	return result;
    344 }
    345 
    346 static s8
    347 stream_to_s8(Stream *s)
    348 {
    349 	s8 result = {.len = s->widx, .data = s->buf};
    350 	return result;
    351 }
    352 
    353 static void
    354 stream_push_byte(Stream *s, u8 cp)
    355 {
    356 	s->errors |= !(s->cap - s->widx);
    357 	if (!s->errors)
    358 		s->buf[s->widx++] = cp;
    359 }
    360 
    361 static void
    362 stream_push_s8(Stream *s, s8 str)
    363 {
    364 	s->errors |= (s->cap - s->widx) < str.len;
    365 	if (!s->errors) {
    366 		for (size i = 0; i < str.len; i++)
    367 			s->buf[s->widx++] = str.data[i];
    368 	}
    369 }
    370 
    371 /* NOTE: how can he get away with not using a library for this */
    372 static void
    373 stream_push_s8_left_padded(Stream *s, s8 str, u32 width)
    374 {
    375 	for (u32 i = str.len; i < width; i++)
    376 		stream_push_byte(s, ' ');
    377 	stream_push_s8(s, str);
    378 }
    379 
    380 static void
    381 stream_push_s8s(Stream *s, u32 count, s8 *strs)
    382 {
    383 	while (count) { stream_push_s8(s, *strs++); count--; }
    384 }
    385 
    386 static void
    387 stream_push_hex_u64(Stream *s, u64 n)
    388 {
    389 	if (!s->errors) {
    390 		static u8 hex[16] = {"0123456789abcdef"};
    391 		u8  buf[16];
    392 		u8 *end = buf + sizeof(buf);
    393 		u8 *beg = end;
    394 		while (n) {
    395 			*--beg = hex[n & 0x0F];
    396 			n >>= 4;
    397 		}
    398 		while (end - beg < 2)
    399 			*--beg = '0';
    400 		stream_push_s8(s, (s8){.len = end - beg, .data = beg});
    401 	}
    402 }
    403 
    404 static void
    405 stream_push_u64_padded(Stream *s, u64 n, u32 width)
    406 {
    407 	if (!s->errors) {
    408 		u8 tmp[64];
    409 		u8 *end = tmp + sizeof(tmp);
    410 		u8 *beg = end;
    411 		do { *--beg = '0' + (n % 10); } while (n /= 10);
    412 
    413 		s8 str = {.len = end - beg, .data = beg};
    414 		for (u32 i = str.len; i < width; i++)
    415 			stream_push_byte(s, ' ');
    416 		stream_push_s8(s, str);
    417 	}
    418 }
    419 
    420 static void
    421 stream_push_u64(Stream *s, u64 n)
    422 {
    423 	stream_push_u64_padded(s, n, 0);
    424 }
    425 
    426 static void
    427 stream_push_i64(Stream *s, i64 n)
    428 {
    429 	s->errors |= n == I64_MIN;
    430 	if (!s->errors) {
    431 		if (n < 0) {
    432 			stream_push_byte(s, '-');
    433 			n = -n;
    434 		}
    435 		stream_push_u64_padded(s, n, 0);
    436 	}
    437 }
    438 
    439 static void
    440 stream_push_iv2(Stream *s, iv2 v)
    441 {
    442 	stream_push_byte(s, '{');
    443 	stream_push_i64(s, v.x);
    444 	stream_push_byte(s, ',');
    445 	stream_push_i64(s, v.y);
    446 	stream_push_byte(s, '}');
    447 }
    448 
    449 static void
    450 stream_push_f64(Stream *s, f64 f, i64 prec)
    451 {
    452 	if (f < 0) {
    453 		stream_push_byte(s, '-');
    454 		f *= -1;
    455 	}
    456 
    457 	/* NOTE: round last digit */
    458 	f += 0.5f / prec;
    459 
    460 	if (f >= (f64)(-1UL >> 1)) {
    461 		stream_push_s8(s, s8("inf"));
    462 	} else {
    463 		u64 integral = f;
    464 		u64 fraction = (f - integral) * prec;
    465 		stream_push_u64(s, integral);
    466 		stream_push_byte(s, '.');
    467 		for (u64 i = prec / 10; i > 1; i /= 10) {
    468 			if (i > fraction)
    469 				stream_push_byte(s, '0');
    470 		}
    471 		stream_push_u64(s, fraction);
    472 	}
    473 }
    474 
    475 static SelectionIterator
    476 selection_iterator(Range s, Row *rows, u32 term_width)
    477 {
    478 	SelectionIterator result;
    479 	result.rows       = rows;
    480 	result.cursor     = (iv2){.x = -1, .y = -1};
    481 	result.next       = s.start;
    482 	result.end        = s.end;
    483 	result.term_width = term_width;
    484 	return result;
    485 }
    486 
    487 static Cell *
    488 selection_next(SelectionIterator *s)
    489 {
    490 	Cell *result = 0;
    491 
    492 	if (!equal_iv2(s->cursor, s->end)) {
    493 		s->cursor = s->next;
    494 		result    = s->rows[s->next.y] + s->next.x++;
    495 		if (s->next.x == s->term_width) {
    496 			s->next.x = 0;
    497 			s->next.y++;
    498 		}
    499 	}
    500 
    501 	return result;
    502 }
    503 
    504 static s8
    505 envp_lookup(s8 name, char **e)
    506 {
    507 	ASSERT(name.data[name.len - 1] == '=');
    508 
    509 	s8 s, result = {0};
    510 	for (; *e; e++) {
    511 		s = c_str_to_s8(*e);
    512 		if (s8_prefix_of(name, s))
    513 			break;
    514 	}
    515 
    516 	if (*e) {
    517 		result = s;
    518 		result.len  -= name.len;
    519 		result.data += name.len;
    520 	}
    521 
    522 	return result;
    523 }
    524 
    525 static c8 **
    526 construct_c_str_array(Arena *a, SLLVariableVector vars)
    527 {
    528 	c8 **result = alloc(a, c8 *, vars.count + 1);
    529 
    530 	VariableLink *chain = vars.next;
    531 	for (u32 i = 0; i < vars.count; i++) {
    532 		ASSERT(chain->var->type == VT_S8);
    533 		result[i] = (c8 *)chain->var->s8.data;
    534 		chain     = chain->next;
    535 	}
    536 
    537 	return result;
    538 }
    539 
    540 static b32
    541 any_mouse_down(TerminalInput *input)
    542 {
    543 	b32 result = input->keys[MOUSE_LEFT].ended_down   ||
    544 	             input->keys[MOUSE_RIGHT].ended_down  ||
    545 	             input->keys[MOUSE_MIDDLE].ended_down;
    546 	return result;
    547 }
    548 
    549 static b32
    550 all_mouse_up(TerminalInput *input)
    551 {
    552 	b32 result = !input->keys[MOUSE_LEFT].ended_down   &&
    553 	             !input->keys[MOUSE_RIGHT].ended_down  &&
    554 	             !input->keys[MOUSE_MIDDLE].ended_down;
    555 	return result;
    556 }
    557 
    558 static void
    559 button_action(ButtonState *button, b32 pressed)
    560 {
    561 	if (pressed != button->ended_down)
    562 		button->transitions++;
    563 	button->ended_down = pressed;
    564 }
    565 
    566 static b32
    567 pressed_last_frame(ButtonState *button)
    568 {
    569 	b32 result = (button->transitions > 1) || (button->ended_down && button->transitions == 1);
    570 	return result;
    571 }
    572 
    573 static s8
    574 utf8_encode(u32 cp)
    575 {
    576 	static u8 buf[4];
    577 	s8 ret = { .data = buf, .len = -1 };
    578 	if (cp < 0x80) {
    579 		ret.len = 1;
    580 		buf[0] = cp & 0x7F;
    581 	} else if (cp < 0x800) {
    582 		ret.len = 2;
    583 		buf[0] = ((cp >>  6) & 0x1F) | 0xC0;
    584 		buf[1] = ((cp >>  0) & 0x3F) | 0x80;
    585 	} else if (cp < 0x10000) {
    586 		ret.len = 3;
    587 		buf[0] = ((cp >> 12) & 0x0F) | 0xE0;
    588 		buf[1] = ((cp >>  6) & 0x3F) | 0x80;
    589 		buf[2] = ((cp >>  0) & 0x3F) | 0x80;
    590 	} else if (cp < 0x200000) {
    591 		ret.len = 4;
    592 		buf[0] = ((cp >> 18) & 0x07) | 0xF0;
    593 		buf[1] = ((cp >> 12) & 0x3F) | 0x80;
    594 		buf[2] = ((cp >>  6) & 0x3F) | 0x80;
    595 		buf[3] = ((cp >>  0) & 0x3F) | 0x80;
    596 	}
    597 	return ret;
    598 }
    599 
    600 #include "extern/utf8_decode.c"