Unified · Split
| @@ -0,0 +1 @@ | |||
| 1 | add `term-clip-cc` | ||
| @@ -3377,6 +3377,11 @@ Property list · 3376 unmodified lines | |||
| 3377 | See ``--cursor-autohide``. Setting this to a new value will always update | 3377 | See ``--cursor-autohide``. Setting this to a new value will always update |
| 3378 | the cursor, and reset the internal timer. | 3378 | the cursor, and reset the internal timer. |
| 3379 | 3379 | ||
| 3380 | ``term-clip-cc`` | ||
| 3381 | Inserts the symbol to force line truncation to the current terminal width. | ||
| 3382 | This can be used for ``show-text`` and other OSD messages. It must be the | ||
| 3383 | first character in the line. It takes effect until the end of the line. | ||
| 3384 | |||
| 3380 | ``osd-sym-cc`` | 3385 | ``osd-sym-cc`` |
| 3381 | Inserts the current OSD symbol as opaque OSD control code (cc). This makes | 3386 | Inserts the current OSD symbol as opaque OSD control code (cc). This makes |
| 3382 | sense only with the ``show-text`` command or options which set OSD messages. | 3387 | sense only with the ``show-text`` command or options which set OSD messages. |
| @@ -374,7 +374,7 @@ static bool test_terminal_level(struct mp_log *log, int lev) · 373 unmodified lines | |||
| 374 | } | 374 | } |
| 375 | 375 | ||
| 376 | static void append_terminal_line(struct mp_log *log, int lev, | 376 | static void append_terminal_line(struct mp_log *log, int lev, |
| 377 | bstr text, bstr *term_msg, int *line_w) | 377 | bstr text, bstr *term_msg, int *line_w, int term_w) |
| 378 | { | 378 | { |
| 379 | struct mp_log_root *root = log->root; | 379 | struct mp_log_root *root = log->root; |
| 380 | 380 | ||
| @@ -394,8 +394,35 @@ static void append_terminal_line(struct mp_log *log, int lev, · 13 unmodified lines | |||
| 394 | } | 394 | } |
| 395 | 395 | ||
| 396 | bstr_xappend(root, term_msg, text); | 396 | bstr_xappend(root, term_msg, text); |
| 397 | |||
| 398 | const unsigned char *cut_pos = NULL; | ||
| 399 | int width = term_disp_width(bstr_splice(*term_msg, start, term_msg->len), | ||
| 400 | term_w - 3, &cut_pos); | ||
| 401 | if (cut_pos) { | ||
| 402 | int new_len = cut_pos - term_msg->start; | ||
| 403 | bstr rem = {(unsigned char *)cut_pos, term_msg->len - new_len}; | ||
| 404 | term_msg->len = new_len; | ||
| 405 | |||
| 406 | bstr_xappend(root, term_msg, bstr0("...")); | ||
| 407 | |||
| 408 | while (rem.len) { | ||
| 409 | if (bstr_eatstart0(&rem, "\033[")) { | ||
| 410 | bstr_xappend(root, term_msg, bstr0("\033[")); | ||
| 411 | |||
| 412 | while (rem.len && !((*rem.start >= '@' && *rem.start <= '~') || *rem.start == 'm')) { | ||
| 413 | bstr_xappend(root, term_msg, bstr_splice(rem, 0, 1)); | ||
| 414 | rem = bstr_cut(rem, 1); | ||
| 415 | } | ||
| 416 | bstr_xappend(root, term_msg, bstr_splice(rem, 0, 1)); | ||
| 417 | } | ||
| 418 | rem = bstr_cut(rem, 1); | ||
| 419 | } | ||
| 420 | |||
| 421 | bstr_xappend(root, term_msg, bstr0("\n")); | ||
| 422 | width += 3; | ||
| 423 | } | ||
| 397 | *line_w = root->isatty[term_msg_fileno(root, lev)] | 424 | *line_w = root->isatty[term_msg_fileno(root, lev)] |
| 398 | ? term_disp_width(bstr_splice(*term_msg, start, term_msg->len)) : 0; | 425 | ? width : 0; |
| 399 | } | 426 | } |
| 400 | 427 | ||
| 401 | static struct mp_log_buffer_entry *log_buffer_read(struct mp_log_buffer *buffer) | 428 | static struct mp_log_buffer_entry *log_buffer_read(struct mp_log_buffer *buffer) |
| @@ -496,7 +523,8 @@ static void write_term_msg(struct mp_log *log, int lev, bstr text, bstr *out) · 94 unmodified lines | |||
| 496 | 523 | ||
| 497 | if (print_term) { | 524 | if (print_term) { |
| 498 | int line_w; | 525 | int line_w; |
| 499 | append_terminal_line(log, lev, line, &root->term_msg_tmp, &line_w); | 526 | append_terminal_line(log, lev, line, &root->term_msg_tmp, &line_w, |
| 527 | bstr_eatstart0(&line, TERM_MSG_0) ? term_w : INT_MAX); | ||
| 500 | term_msg_lines += (!line_w || !term_w) | 528 | term_msg_lines += (!line_w || !term_w) |
| 501 | ? 1 : (line_w + term_w - 1) / term_w; | 529 | ? 1 : (line_w + term_w - 1) / term_w; |
| 502 | } | 530 | } |
| @@ -506,7 +534,8 @@ static void write_term_msg(struct mp_log *log, int lev, bstr text, bstr *out) · 3 unmodified lines | |||
| 506 | if (lev == MSGL_STATUS) { | 534 | if (lev == MSGL_STATUS) { |
| 507 | int line_w = 0; | 535 | int line_w = 0; |
| 508 | if (str.len && print_term) | 536 | if (str.len && print_term) |
| 509 | append_terminal_line(log, lev, str, &root->term_msg_tmp, &line_w); | 537 | append_terminal_line(log, lev, str, &root->term_msg_tmp, &line_w, |
| 538 | bstr_eatstart0(&str, TERM_MSG_0) ? term_w : INT_MAX); | ||
| 510 | term_msg_lines += !term_w ? (str.len ? 1 : 0) | 539 | term_msg_lines += !term_w ? (str.len ? 1 : 0) |
| 511 | : (line_w + term_w - 1) / term_w; | 540 | : (line_w + term_w - 1) / term_w; |
| 512 | } else if (str.len) { | 541 | } else if (str.len) { |
| @@ -25,6 +25,8 @@ · 24 unmodified lines | |||
| 25 | 25 | ||
| 26 | #include "osdep/compiler.h" | 26 | #include "osdep/compiler.h" |
| 27 | 27 | ||
| 28 | #define TERM_MSG_0 "\xFC" | ||
| 29 | |||
| 28 | struct mp_log; | 30 | struct mp_log; |
| 29 | 31 | ||
| 30 | // A mp_log instance that never outputs anything. | 32 | // A mp_log instance that never outputs anything. |
| @@ -662,20 +662,26 @@ static int ucdToCharacterWidth(const int val) · 661 unmodified lines | |||
| 662 | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. | 662 | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
| 663 | */ | 663 | */ |
| 664 | 664 | ||
| 665 | int term_disp_width(bstr str) | 665 | #include "common/common.h" |
| 666 | |||
| 667 | int term_disp_width(bstr str, int max_width, const unsigned char **cut_pos) | ||
| 666 | { | 668 | { |
| 667 | static const int ambiguous_width = 1; | 669 | static const int ambiguous_width = 1; |
| 668 | 670 | ||
| 669 | int width = 0; | 671 | int width = 0; |
| 670 | 672 | ||
| 673 | const unsigned char *prev_pos = str.start; | ||
| 671 | while (str.len) { | 674 | while (str.len) { |
| 675 | int current_width = 0; | ||
| 676 | |||
| 672 | if (bstr_eatstart0(&str, "\033[")) { | 677 | if (bstr_eatstart0(&str, "\033[")) { |
| 673 | while (str.len && !((*str.start >= '@' && *str.start <= '~') || *str.start == 'm')) | 678 | while (str.len && !((*str.start >= '@' && *str.start <= '~') || *str.start == 'm')) |
| 674 | str = bstr_cut(str, 1); | 679 | str = bstr_cut(str, 1); |
| 675 | str = bstr_cut(str, 1); | 680 | str = bstr_cut(str, 1); |
| 676 | continue; | 681 | goto next; |
| 677 | } | 682 | } |
| 678 | 683 | ||
| 684 | prev_pos = str.start; | ||
| 679 | int cp = bstr_decode_utf8(str, &str); | 685 | int cp = bstr_decode_utf8(str, &str); |
| 680 | 686 | ||
| 681 | // Stop processing on any invalid input | 687 | // Stop processing on any invalid input |
| @@ -684,18 +690,17 @@ int term_disp_width(bstr str) · 2 unmodified lines | |||
| 684 | 690 | ||
| 685 | if (cp == '\r') { | 691 | if (cp == '\r') { |
| 686 | width = 0; | 692 | width = 0; |
| 687 | continue; | 693 | goto next; |
| 688 | } | 694 | } |
| 689 | 695 | ||
| 690 | if (cp < 0x20) | 696 | if (cp < 0x20) |
| 691 | continue; | 697 | goto next; |
| 692 | 698 | ||
| 693 | if (cp <= 0x7E) { | 699 | if (cp <= 0x7E) { |
| 694 | width++; | 700 | current_width = 1; |
| 695 | continue; | 701 | goto next; |
| 696 | } | 702 | } |
| 697 | 703 | ||
| 698 | int grapheme_width = 0; | ||
| 699 | int state = 0; | 704 | int state = 0; |
| 700 | 705 | ||
| 701 | while (true) { | 706 | while (true) { |
| @@ -711,7 +716,7 @@ int term_disp_width(bstr str) · 9 unmodified lines | |||
| 711 | if (cp == 0xFE0F) | 716 | if (cp == 0xFE0F) |
| 712 | w = 2; | 717 | w = 2; |
| 713 | 718 | ||
| 714 | grapheme_width += w; | 719 | current_width += w; |
| 715 | 720 | ||
| 716 | if (!str.len) | 721 | if (!str.len) |
| 717 | break; | 722 | break; |
| @@ -730,7 +735,20 @@ int term_disp_width(bstr str) · 12 unmodified lines | |||
| 730 | 735 | ||
| 731 | str = cluster_end; | 736 | str = cluster_end; |
| 732 | } | 737 | } |
| 733 | width += grapheme_width > 2 ? 2 : grapheme_width; | 738 | |
| 739 | next: | ||
| 740 | current_width = MPMIN(current_width, 2); | ||
| 741 | if (width + current_width > max_width) { | ||
| 742 | assert(prev_pos < str.start + str.len); | ||
| 743 | *cut_pos = prev_pos; | ||
| 744 | break; | ||
| 745 | } | ||
| 746 | width += current_width; | ||
| 747 | if (width == max_width) { | ||
| 748 | if (str.len) | ||
| 749 | *cut_pos = str.start; | ||
| 750 | break; | ||
| 751 | } | ||
| 734 | } | 752 | } |
| 735 | 753 | ||
| 736 | return width; | 754 | return width; |
| @@ -6,6 +6,9 @@ · 5 unmodified lines | |||
| 6 | * @brief Determines the number of columns required to display a given string. | 6 | * @brief Determines the number of columns required to display a given string. |
| 7 | * | 7 | * |
| 8 | * @param str Sequence of UTF-8 chars | 8 | * @param str Sequence of UTF-8 chars |
| 9 | * @param max_width Maximum allowed width of string | ||
| 10 | * @param cut_pos If max_width is exceeded, this will be initialized to last | ||
| 11 | * full printable character before width limit. | ||
| 9 | * @return int width of the string | 12 | * @return int width of the string |
| 10 | */ | 13 | */ |
| 11 | int term_disp_width(bstr str); | 14 | int term_disp_width(bstr str, int max_width, const unsigned char **cut_pos); |
| @@ -2926,6 +2926,12 @@ static int mp_property_osd_ass(void *ctx, struct m_property *prop, · 2925 unmodified lines | |||
| 2926 | return m_property_read_sub(props, action, arg); | 2926 | return m_property_read_sub(props, action, arg); |
| 2927 | } | 2927 | } |
| 2928 | 2928 | ||
| 2929 | static int mp_property_term_clip(void *ctx, struct m_property *prop, | ||
| 2930 | int action, void *arg) | ||
| 2931 | { | ||
| 2932 | return m_property_strdup_ro(action, arg, TERM_MSG_0); | ||
| 2933 | } | ||
| 2934 | |||
| 2929 | static int mp_property_term_size(void *ctx, struct m_property *prop, | 2935 | static int mp_property_term_size(void *ctx, struct m_property *prop, |
| 2930 | int action, void *arg) | 2936 | int action, void *arg) |
| 2931 | { | 2937 | { |
| @@ -4159,6 +4165,8 @@ static const struct m_property mp_properties_base[] = { · 1227 unmodified lines | |||
| 4159 | {"osd-sym-cc", mp_property_osd_sym}, | 4165 | {"osd-sym-cc", mp_property_osd_sym}, |
| 4160 | {"osd-ass-cc", mp_property_osd_ass}, | 4166 | {"osd-ass-cc", mp_property_osd_ass}, |
| 4161 | 4167 | ||
| 4168 | {"term-clip-cc", mp_property_term_clip}, | ||
| 4169 | |||
| 4162 | {"mouse-pos", mp_property_mouse_pos}, | 4170 | {"mouse-pos", mp_property_mouse_pos}, |
| 4163 | {"touch-pos", mp_property_touch_pos}, | 4171 | {"touch-pos", mp_property_touch_pos}, |
| 4164 | 4172 | ||
| @@ -207,6 +207,10 @@ void osd_mangle_ass(bstr *dst, const char *in, bool replace_newlines) · 206 unmodified lines | |||
| 207 | in += 1; | 207 | in += 1; |
| 208 | continue; | 208 | continue; |
| 209 | } | 209 | } |
| 210 | if (*in == TERM_MSG_0[0]) { | ||
| 211 | in += 1; | ||
| 212 | continue; | ||
| 213 | } | ||
| 210 | if (escape_ass && *in == '{') | 214 | if (escape_ass && *in == '{') |
| 211 | bstr_xappend(NULL, dst, bstr0("\\")); | 215 | bstr_xappend(NULL, dst, bstr0("\\")); |
| 212 | // Replace newlines with \N for escape-ass. This is necessary to apply | 216 | // Replace newlines with \N for escape-ass. This is necessary to apply |
| @@ -17,9 +17,11 @@ · 16 unmodified lines | |||
| 17 | 17 | ||
| 18 | #include "test_utils.h" | 18 | #include "test_utils.h" |
| 19 | 19 | ||
| 20 | #include <limits.h> | ||
| 21 | |||
| 20 | #include "misc/codepoint_width.h" | 22 | #include "misc/codepoint_width.h" |
| 21 | 23 | ||
| 22 | #define W(s) term_disp_width((bstr)bstr0_lit(s)) | 24 | #define W(s) term_disp_width((bstr)bstr0_lit(s), INT_MAX, &(const unsigned char *){NULL}) |
| 23 | 25 | ||
| 24 | int main(void) { | 26 | int main(void) { |
| 25 | assert_int_equal(W("A"), 1); // Single ASCII character | 27 | assert_int_equal(W("A"), 1); // Single ASCII character |
| @@ -64,4 +66,23 @@ int main(void) { · 38 unmodified lines | |||
| 64 | 66 | ||
| 65 | // ASCII characters with carriage return | 67 | // ASCII characters with carriage return |
| 66 | assert_int_equal(W("ABC\rDEF"), 3); | 68 | assert_int_equal(W("ABC\rDEF"), 3); |
| 69 | |||
| 70 | bstr str = bstr0("ABCDEF"); | ||
| 71 | const unsigned char *cut_pos; | ||
| 72 | |||
| 73 | cut_pos = NULL; | ||
| 74 | assert_int_equal(term_disp_width(str, 3, &cut_pos), 3); | ||
| 75 | assert_int_equal(cut_pos - str.start, 3); | ||
| 76 | |||
| 77 | cut_pos = NULL; | ||
| 78 | assert_int_equal(term_disp_width(str, -2, &cut_pos), 0); | ||
| 79 | assert_int_equal(cut_pos - str.start, 0); | ||
| 80 | |||
| 81 | cut_pos = NULL; | ||
| 82 | assert_int_equal(term_disp_width(str, str.len, &cut_pos), 6); | ||
| 83 | if (cut_pos) { | ||
| 84 | printf("%s:%d: cut_pos != NULL\n", __FILE__, __LINE__); | ||
| 85 | fflush(stdout); | ||
| 86 | abort(); | ||
| 87 | } | ||
| 67 | } | 88 | } |