[BRLTTY] Low-level BrlAPI questions

Aura Kelloniemi kaura.dev at sange.fi
Thu Apr 15 09:34:24 EDT 2021


On 2021-04-15 at 08:55 -0400, Dave Mielke <Dave at mielke.cc> wrote:
 > [quoted lines by Aura Kelloniemi on 2021/04/15 at 15:36 +0300]
 > >If brlapi__writeTextUtf8 is added so that it does not rely on the
 > >NUL-terminator but has a size parameter, I am done with this discussion.

 > Well, I think it should be easy to write a UTF-8 string, and easy shouldn't require the programmer to have to calculate the UTF-8 character count. The programmer should, in the simple case, still be able to pass nothing more than a quoted string.

Size is a dreaded word in this discussion because of its ambiguity. In the
paragraph quoted above, I used the word size to mean the size of the text
string in bytes, and not the number of Unicode code points it contains.

Anyways, for a C programmer it certainly is easiest to pass in a quoted
string, and let the library to call strlen on it. But a binding writer needs
to copy the user-supplied string to a newly allocated memory location in heap,
append the NUL byte, and then call the write function, if the write function
does not provide a size argument.

I paste here my implementation of brlapi__writeText which is quite complex,
because it avoids copying the input string, if it contains the right number of
characters. This works, but is inefficient. In the code below, write_generic
calls brlapi__write with parameter and result value marshalling. Self is the
wrapped BrlAPI connection object.

    pub fn write_text<S: AsRef<str>>(&self, text: S, cursor: Cursor) -> Result<()> {
        let text = text.as_ref(); // Allow passing in a String object or a string reference
        let size = self.get_display_size_cells()?; // cols * rows

        // Find the number of characters in text and the last index that will fit the display.
        let mut chars = 0;
        let mut end_index = None;
        for (idx, ch) in text.char_indices() {
            chars += 1;
            if chars == size {
                end_index = Some(idx + ch.len_utf8());
                break;
            }
        }

        if chars < size {
            // The text is too short for the display, and needs to be padded
            let missing = size - chars;

            // Copy text to an a 8-bit integer vector and pad it to fill the whole display
            let bytes_size = text.len() + missing;
            let mut bytes = Vec::with_capacity(bytes_size); // Allocate vector
            bytes.extend_from_slice(text.as_bytes()); // Do the copy
            bytes.resize(bytes_size, b' '); // Pad text. Th character used here does not really matter

            // Use and_mask to hide all characters after the end of text
            let mut and_mask = Vec::with_capacity(size); // Allocate andMask
            and_mask.resize(chars, 0b1111_1111); // Pass all user text as is
            and_mask.resize(size, 0); // Mask away everything after the real text ends

            self.write_generic(
                None, // Display number
                0, // regionBegin
                size, // regionSize
                Some(&bytes), // text and textSize
                Some(&and_mask), // andMask
                None, // orMask
                cursor, // Cursor position
                Some("utf-8"), // Character set
            )
        } else {
            // Text either fit exactly or is too long. Pass a substring to BrlAPI.
            let end_index = end_index.unwrap_or_else(|| text.len()); // Choose the last showable character's index or the text's length
            self.write_generic(
                None, // Display number
                0, // regionBegin
                chars, // regionSize
                Some(&text.as_bytes()[..end_index]), // text and textSize
                None, // andMask
                None, // orMask
                cursor, // Cursor position
                Some("utf-8"), // Character set
            )
        }
    }

-- 
Aura


More information about the BRLTTY mailing list