Files
uiuc-ece391-mp3/student-distrib/lib/chinese_input.c

199 lines
8.3 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "chinese_input.h"
#include "../devices/qemu_vga.h"
#include "../devices/keyboard.h"
#include "../interrupts/multiprocessing.h"
/* void chinese_input_keystroke(uint8_t ch)
* @input: ch - keystroke
* @output: updates multiple state of Chinese IME
* @description: handles key event from keyboard interrupt handler.
* Should always be wrapped in ONTO_DISPLAY_WRAP.
* Supports typing, deleting (backspace), pageup/down (,/.),
* candidate choosing (1-9).
*/
void chinese_input_keystroke(uint8_t ch) {
volatile chinese_input_buf_t* b = &terminals[displayed_terminal_id].chinese_input_buf;
if(ch == BACKSPACE) {
// Remove a character from IME buffer
if(b->buf_len > 0) {
b->buf_len--;
b->buf[b->buf_len] = 0;
// Trigger search of pinyin
chinese_input_search();
} else return;
} else if(ch == ',') {
// Switch to previous page if possible
if(b->page > 0) b->page--;
} else if(ch == '.') {
// Switch to next page if possible
if((b->page + 1) * CHINESE_INPUT_CANDIDATES < b->len) {
b->page++;
}
} else if(ch >= '1' && ch <= '9') {
// Selecting a candidate word, first check if the word is in range.
int i = b->pos + b->page * CHINESE_INPUT_CANDIDATES + ch - '1';
if(i >= b->pos + b->len) return;
// Encode it to 3-byte UTF-8 encoding.
uint16_t code = pinyin_data[i];
uint8_t encode[3];
if(code < 0x800) return; // 2 bytes code, doesn't need to be supported
// UTF-8 3 bytes code: 1110xxxx 10xxxxxx 10xxxxxx
encode[0] = 0xe0 | (code >> 12);
encode[1] = 0x80 | ((code >> 6) & 0x3f);
encode[2] = 0x80 | (code & 0x3f);
// Allocate 3 bytes in keyboard buffer, and copy the character in
if(terminals[displayed_terminal_id].keyboard_buffer_top + 3 >= KEYBOARD_BUFFER_SIZE) return;
terminals[displayed_terminal_id].keyboard_buffer[terminals[displayed_terminal_id].keyboard_buffer_top] = encode[0];
terminals[displayed_terminal_id].keyboard_buffer[terminals[displayed_terminal_id].keyboard_buffer_top + 1] = encode[1];
terminals[displayed_terminal_id].keyboard_buffer[terminals[displayed_terminal_id].keyboard_buffer_top + 2] = encode[2];
terminals[displayed_terminal_id].keyboard_buffer_top += 3;
// Also display it onto screen
putc(encode[0]);
putc(encode[1]);
putc(encode[2]);
// Clear IME state for next input
b->buf_len = 0;
b->pos = b->len = b->page = 0;
} else if(b->buf_len == CHINESE_INPUT_BUF_LEN) {
// Buffer is full, no more typing allowed
return;
} else {
// Transform uppercase to undercase
if(ch >= 'A' && ch <= 'Z') ch = ch - 'A' + 'a';
// Only allow letters
if(ch < 'a' || ch > 'z') return;
// Add letter into buf
b->buf[b->buf_len] = ch;
b->buf_len++;
// Trigger search
chinese_input_search();
}
// Trigger refresh of IME UI
chinese_input_draw();
}
/* void chinese_input_search()
* @input: chinese_input_buf - IME state of current displayed terminal.
* @output: a list of characters is stored in the current state.
* @description: searches for the characters that have the same pinyin
* as stored in buffer, and updates position, length, etc into IME state.
*/
void chinese_input_search() {
volatile chinese_input_buf_t* b = &terminals[displayed_terminal_id].chinese_input_buf;
// If input is empty, clear everything
if(b->buf_len == 0) {
b->pos = b->len = b->page = 0;
return;
}
pinyin_index_t* ptr = pinyin_index;
while(ptr->len != 0) {
// Find the exact match of pinyin
// (Fuzzy pinyin needs a lot more work, won't be implemented)
if((strlen(ptr->pinyin) == b->buf_len)
&& (0 == strncmp((const char*) b->buf, ptr->pinyin, b->buf_len))) {
// Found the pinyin in database
b->pos = ptr->pos;
b->len = ptr->len;
b->page = 0;
return;
}
ptr++;
}
// Nothing found, clear everything
b->pos = b->len = b->page = 0;
}
/* void chinese_input_draw_utf8_char(uint16_t x, uint16_t y, uint16_t code, uint8_t attr)
* @input: x, y - coordinate of the UTF-8 character
* code - 16 bit code (NOT 3-byte huffman) for character
* attr - foreground and background color attribute
* @output: character printed with given settings
* @description: prints a UTF-8 character at the given position.
* Used by IME redraw routine to draw the candidate characters.
*/
void chinese_input_draw_utf8_char(uint16_t x, uint16_t y, uint16_t code, uint8_t attr) {
uint8_t encode[3];
if(code < 0x800) return; // 2 bytes code, doesn't need to be supported
// UTF-8 3 bytes code: 1110xxxx 10xxxxxx 10xxxxxx
encode[0] = 0xe0 | (code >> 12);
encode[1] = 0x80 | ((code >> 6) & 0x3f);
encode[2] = 0x80 | (code & 0x3f);
// Transfer 3 bytes one by one
qemu_vga_putc(x, y, encode[0],
qemu_vga_get_terminal_color(attr),
qemu_vga_get_terminal_color(attr >> 4));
qemu_vga_putc(x, y, encode[1],
qemu_vga_get_terminal_color(attr),
qemu_vga_get_terminal_color(attr >> 4));
qemu_vga_putc(x, y, encode[2],
qemu_vga_get_terminal_color(attr),
qemu_vga_get_terminal_color(attr >> 4));
}
/* void chinese_input_draw()
* @output: IME UI gets refreshed on current display
* @description: redraws the bar of IME.
* Only QEMU VGA will use this area, so putc() isn't used.
*/
void chinese_input_draw() {
volatile chinese_input_buf_t* b = &terminals[displayed_terminal_id].chinese_input_buf;
int i;
// Draw user input section
for(i = 0; i < CHINESE_INPUT_BUF_LEN; i++) {
qemu_vga_putc(i * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
(i < b->buf_len) ? b->buf[i] : ' ',
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_BUF),
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_BUF >> 4));
}
// Draw candidate section
for(i = 0; i < CHINESE_INPUT_CANDIDATES; i++) {
int x = CHINESE_INPUT_BUF_LEN + CHINESE_INPUT_CANDIDATE_WIDTH * i;
if(b->page * CHINESE_INPUT_CANDIDATES + i < b->len) {
qemu_vga_putc(x * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
'1' + i,
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE),
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE >> 4));
qemu_vga_putc((x + 1) * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
'.',
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE),
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE >> 4));
// There is a candidate character here
chinese_input_draw_utf8_char((x + 2) * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
pinyin_data[b->pos + b->page * CHINESE_INPUT_CANDIDATES + i],
CHINESE_INPUT_ATTR_CANDIDATE);
} else {
// No candidate here, clear the position with 4 spaces
qemu_vga_putc(x * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
' ',
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE),
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE >> 4));
qemu_vga_putc((x + 1) * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
' ',
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE),
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE >> 4));
qemu_vga_putc((x + 2) * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
' ',
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE),
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE >> 4));
qemu_vga_putc((x + 3) * FONT_ACTUAL_WIDTH,
CHINESE_INPUT_Y * FONT_ACTUAL_HEIGHT,
' ',
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE),
qemu_vga_get_terminal_color(CHINESE_INPUT_ATTR_CANDIDATE >> 4));
}
}
}