Skip to content

Commit cd711e3

Browse files
committed
Console: port Ironwail mouse support (text selection, clipboard copy)
- Drag to select console text with word snapping on double click, line selection on triple click, select all on quadruple click - Auto-scroll with quadratic easing when dragging past the edge - Ctrl+C/Ctrl+Ins copies the selection (converted from Quake charset to UTF-8), Ctrl+A selects the whole buffer - OS cursor is shown while the console is open (also in fullscreen) and switches to an I-beam over selectable text - Selection highlight drawn behind the console text
1 parent e5da75f commit cd711e3

13 files changed

Lines changed: 991 additions & 8 deletions

File tree

Quake/common.c

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,103 @@ size_t UTF8_WriteCodePoint (char *dst, size_t maxbytes, uint32_t codepoint)
429429
return 0;
430430
}
431431

432+
// clang-format off
433+
static const uint32_t qchar_to_unicode[256] =
434+
{/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
435+
----------------------------------------------------------------------------------------------------------------------------------
436+
0 */ 0x00B7, 0, 0, 0, 0, 0x00B7, 0, 0, 0, 0, '\n', 0x25A0, ' ', 0x25B6, 0x00B7, 0x00B7, /*
437+
1 */ 0x301A, 0x301B, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0x00B7, '-', '-', '-', /*
438+
2 */ ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', /*
439+
3 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', /*
440+
4 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /*
441+
5 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', /*
442+
6 */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /*
443+
7 */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x2190, /*
444+
445+
8 */ '-', '-', '-', '-', 0, 0x2022, 0, 0, 0, 0, '\n', 0x25A0, ' ', 0x25B6, 0x2022, 0x2022, /*
446+
9 */ 0x301A, 0x301B, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0x2022, '-', '-', '-', /*
447+
10 */ ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', /*
448+
11 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', /*
449+
12 */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /*
450+
13 */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', /*
451+
14 */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /*
452+
15 */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x2190, /*
453+
----------------------------------------------------------------------------------------------------------------------------------
454+
*/};
455+
// clang-format on
456+
457+
/*
458+
==================
459+
UTF8_CodePointLength
460+
461+
Returns the number of bytes needed to encode the codepoint
462+
using UTF-8 (max 4), or 0 for an invalid code point
463+
==================
464+
*/
465+
size_t UTF8_CodePointLength (uint32_t codepoint)
466+
{
467+
if (codepoint < 0x80)
468+
return 1;
469+
470+
if (codepoint < 0x800)
471+
return 2;
472+
473+
if (codepoint < 0x10000)
474+
return 3;
475+
476+
if (codepoint < 0x110000)
477+
return 4;
478+
479+
return 0;
480+
}
481+
482+
/*
483+
==================
484+
UTF8_FromQuake
485+
486+
Converts a string from Quake encoding to UTF-8
487+
488+
Returns the number of written characters (including the NUL terminator)
489+
if a valid output buffer is provided (dst is non-NULL, maxbytes > 0),
490+
or the total amount of space necessary to encode the entire src string
491+
if dst is NULL and maxbytes is 0.
492+
==================
493+
*/
494+
size_t UTF8_FromQuake (char *dst, size_t maxbytes, const char *src)
495+
{
496+
size_t i, j, written;
497+
498+
if (!maxbytes)
499+
{
500+
if (dst)
501+
return 0; // error
502+
for (i = 0, j = 0; src[i]; i++)
503+
{
504+
uint32_t codepoint = qchar_to_unicode[(unsigned char)src[i]];
505+
if (codepoint)
506+
j += UTF8_CodePointLength (codepoint);
507+
}
508+
return j + 1; // include terminator
509+
}
510+
511+
--maxbytes;
512+
513+
for (i = 0, j = 0; j < maxbytes && src[i]; i++)
514+
{
515+
uint32_t codepoint = qchar_to_unicode[(unsigned char)src[i]];
516+
if (!codepoint)
517+
continue;
518+
written = UTF8_WriteCodePoint (dst + j, maxbytes - j, codepoint);
519+
if (!written)
520+
break;
521+
j += written;
522+
}
523+
524+
dst[j++] = '\0';
525+
526+
return j;
527+
}
528+
432529
char *q_strtrim (char *str)
433530
{
434531
// trim leading and ending whitespaces:

Quake/common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,13 @@ char *q_strupr (char *str);
262262
/* writes the UTF-8 encoding of the code point; returns bytes written (up to 4) or 0 on error */
263263
size_t UTF8_WriteCodePoint (char *dst, size_t maxbytes, uint32_t codepoint);
264264

265+
/* returns the number of bytes needed to encode the code point using UTF-8 (max 4), or 0 for an invalid code point */
266+
size_t UTF8_CodePointLength (uint32_t codepoint);
267+
268+
/* converts a string from Quake encoding to UTF-8; returns the number of written characters (including the NUL terminator)
269+
if a valid output buffer is provided, or the total amount of space necessary if dst is NULL and maxbytes is 0 */
270+
size_t UTF8_FromQuake (char *dst, size_t maxbytes, const char *src);
271+
265272
/* Trim whitespace on both ends, modifying str on-place: Returns the new start of str after trim */
266273
char *q_strtrim (char *str);
267274

0 commit comments

Comments
 (0)