162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* speakup.c 362306a36Sopenharmony_ci * review functions for the speakup screen review package. 462306a36Sopenharmony_ci * originally written by: Kirk Reiser and Andy Berdan. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * extensively modified by David Borowski. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci ** Copyright (C) 1998 Kirk Reiser. 962306a36Sopenharmony_ci * Copyright (C) 2003 David Borowski. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/vt.h> 1462306a36Sopenharmony_ci#include <linux/tty.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> /* __get_free_page() and friends */ 1662306a36Sopenharmony_ci#include <linux/vt_kern.h> 1762306a36Sopenharmony_ci#include <linux/ctype.h> 1862306a36Sopenharmony_ci#include <linux/selection.h> 1962306a36Sopenharmony_ci#include <linux/unistd.h> 2062306a36Sopenharmony_ci#include <linux/jiffies.h> 2162306a36Sopenharmony_ci#include <linux/kthread.h> 2262306a36Sopenharmony_ci#include <linux/keyboard.h> /* for KT_SHIFT */ 2362306a36Sopenharmony_ci#include <linux/kbd_kern.h> /* for vc_kbd_* and friends */ 2462306a36Sopenharmony_ci#include <linux/input.h> 2562306a36Sopenharmony_ci#include <linux/kmod.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* speakup_*_selection */ 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/sched.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci#include <linux/types.h> 3262306a36Sopenharmony_ci#include <linux/consolemap.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/spinlock.h> 3562306a36Sopenharmony_ci#include <linux/notifier.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/uaccess.h> /* copy_from|to|user() and others */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "spk_priv.h" 4062306a36Sopenharmony_ci#include "speakup.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define MAX_DELAY msecs_to_jiffies(500) 4362306a36Sopenharmony_ci#define MINECHOCHAR SPACE 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciMODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>"); 4662306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>"); 4762306a36Sopenharmony_ciMODULE_DESCRIPTION("Speakup console speech"); 4862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4962306a36Sopenharmony_ciMODULE_VERSION(SPEAKUP_VERSION); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cichar *synth_name; 5262306a36Sopenharmony_cimodule_param_named(synth, synth_name, charp, 0444); 5362306a36Sopenharmony_cimodule_param_named(quiet, spk_quiet_boot, bool, 0444); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciMODULE_PARM_DESC(synth, "Synth to start if speakup is built in."); 5662306a36Sopenharmony_ciMODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found."); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cispecial_func spk_special_handler; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cishort spk_pitch_shift, synth_flags; 6162306a36Sopenharmony_cistatic u16 buf[256]; 6262306a36Sopenharmony_ciint spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10; 6362306a36Sopenharmony_ciint spk_no_intr, spk_spell_delay; 6462306a36Sopenharmony_ciint spk_key_echo, spk_say_word_ctl; 6562306a36Sopenharmony_ciint spk_say_ctrl, spk_bell_pos; 6662306a36Sopenharmony_cishort spk_punc_mask; 6762306a36Sopenharmony_ciint spk_punc_level, spk_reading_punc; 6862306a36Sopenharmony_ciint spk_cur_phonetic; 6962306a36Sopenharmony_cichar spk_str_caps_start[MAXVARLEN + 1] = "\0"; 7062306a36Sopenharmony_cichar spk_str_caps_stop[MAXVARLEN + 1] = "\0"; 7162306a36Sopenharmony_cichar spk_str_pause[MAXVARLEN + 1] = "\0"; 7262306a36Sopenharmony_cibool spk_paused; 7362306a36Sopenharmony_ciconst struct st_bits_data spk_punc_info[] = { 7462306a36Sopenharmony_ci {"none", "", 0}, 7562306a36Sopenharmony_ci {"some", "/$%&@", SOME}, 7662306a36Sopenharmony_ci {"most", "$%&#()=+*/@^<>|\\", MOST}, 7762306a36Sopenharmony_ci {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC}, 7862306a36Sopenharmony_ci {"delimiters", "", B_WDLM}, 7962306a36Sopenharmony_ci {"repeats", "()", CH_RPT}, 8062306a36Sopenharmony_ci {"extended numeric", "", B_EXNUM}, 8162306a36Sopenharmony_ci {"symbols", "", B_SYM}, 8262306a36Sopenharmony_ci {NULL, NULL} 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic char mark_cut_flag; 8662306a36Sopenharmony_ci#define MAX_KEY 160 8762306a36Sopenharmony_cistatic u_char *spk_shift_table; 8862306a36Sopenharmony_ciu_char *spk_our_keys[MAX_KEY]; 8962306a36Sopenharmony_ciu_char spk_key_buf[600]; 9062306a36Sopenharmony_ciconst u_char spk_key_defaults[] = { 9162306a36Sopenharmony_ci#include "speakupmap.h" 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* cursor track modes, must be ordered same as cursor_msgs in enum msg_index_t */ 9562306a36Sopenharmony_cienum cursor_track { 9662306a36Sopenharmony_ci CT_Off = 0, 9762306a36Sopenharmony_ci CT_On, 9862306a36Sopenharmony_ci CT_Highlight, 9962306a36Sopenharmony_ci CT_Window, 10062306a36Sopenharmony_ci CT_Max, 10162306a36Sopenharmony_ci read_all_mode = CT_Max, 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* Speakup Cursor Track Variables */ 10562306a36Sopenharmony_cistatic enum cursor_track cursor_track = 1, prev_cursor_track = 1; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct tty_struct *tty; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void spkup_write(const u16 *in_buf, int count); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic char *phonetic[] = { 11262306a36Sopenharmony_ci "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", 11362306a36Sopenharmony_ci "india", "juliett", "keelo", "leema", "mike", "november", "oscar", 11462306a36Sopenharmony_ci "papa", 11562306a36Sopenharmony_ci "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey", 11662306a36Sopenharmony_ci "x ray", "yankee", "zulu" 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* array of 256 char pointers (one for each character description) 12062306a36Sopenharmony_ci * initialized to default_chars and user selectable via 12162306a36Sopenharmony_ci * /proc/speakup/characters 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cichar *spk_characters[256]; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cichar *spk_default_chars[256] = { 12662306a36Sopenharmony_ci/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g", 12762306a36Sopenharmony_ci/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o", 12862306a36Sopenharmony_ci/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w", 12962306a36Sopenharmony_ci/*024*/ "^x", "^y", "^z", "control", "control", "control", "control", 13062306a36Sopenharmony_ci "control", 13162306a36Sopenharmony_ci/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and", 13262306a36Sopenharmony_ci "tick", 13362306a36Sopenharmony_ci/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash", 13462306a36Sopenharmony_ci "dot", 13562306a36Sopenharmony_ci "slash", 13662306a36Sopenharmony_ci/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven", 13762306a36Sopenharmony_ci "eight", "nine", 13862306a36Sopenharmony_ci/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at", 13962306a36Sopenharmony_ci/*065*/ "EIGH", "B", "C", "D", "E", "F", "G", 14062306a36Sopenharmony_ci/*072*/ "H", "I", "J", "K", "L", "M", "N", "O", 14162306a36Sopenharmony_ci/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X", 14262306a36Sopenharmony_ci/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket", 14362306a36Sopenharmony_ci "caret", 14462306a36Sopenharmony_ci "line", 14562306a36Sopenharmony_ci/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g", 14662306a36Sopenharmony_ci/*104*/ "h", "i", "j", "k", "l", "m", "n", "o", 14762306a36Sopenharmony_ci/*112*/ "p", "q", "r", "s", "t", "u", "v", "w", 14862306a36Sopenharmony_ci/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh", 14962306a36Sopenharmony_ci/*127*/ "del", "control", "control", "control", "control", "control", 15062306a36Sopenharmony_ci "control", "control", "control", "control", "control", 15162306a36Sopenharmony_ci/*138*/ "control", "control", "control", "control", "control", 15262306a36Sopenharmony_ci "control", "control", "control", "control", "control", 15362306a36Sopenharmony_ci "control", "control", 15462306a36Sopenharmony_ci/*150*/ "control", "control", "control", "control", "control", 15562306a36Sopenharmony_ci "control", "control", "control", "control", "control", 15662306a36Sopenharmony_ci/*160*/ "nbsp", "inverted bang", 15762306a36Sopenharmony_ci/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section", 15862306a36Sopenharmony_ci/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle", 15962306a36Sopenharmony_ci/*172*/ "not", "soft hyphen", "registered", "macron", 16062306a36Sopenharmony_ci/*176*/ "degrees", "plus or minus", "super two", "super three", 16162306a36Sopenharmony_ci/*180*/ "acute accent", "micro", "pilcrow", "middle dot", 16262306a36Sopenharmony_ci/*184*/ "cedilla", "super one", "male ordinal", "double right angle", 16362306a36Sopenharmony_ci/*188*/ "one quarter", "one half", "three quarters", 16462306a36Sopenharmony_ci "inverted question", 16562306a36Sopenharmony_ci/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT", 16662306a36Sopenharmony_ci "A RING", 16762306a36Sopenharmony_ci/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX", 16862306a36Sopenharmony_ci "E OOMLAUT", 16962306a36Sopenharmony_ci/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH", 17062306a36Sopenharmony_ci "N TILDE", 17162306a36Sopenharmony_ci/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT", 17262306a36Sopenharmony_ci/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE", 17362306a36Sopenharmony_ci "U CIRCUMFLEX", 17462306a36Sopenharmony_ci/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave", 17562306a36Sopenharmony_ci/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring", 17662306a36Sopenharmony_ci/*230*/ "ae", "c cidella", "e grave", "e acute", 17762306a36Sopenharmony_ci/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute", 17862306a36Sopenharmony_ci "i circumflex", 17962306a36Sopenharmony_ci/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute", 18062306a36Sopenharmony_ci "o circumflex", 18162306a36Sopenharmony_ci/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave", 18262306a36Sopenharmony_ci "u acute", 18362306a36Sopenharmony_ci/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut" 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* array of 256 u_short (one for each character) 18762306a36Sopenharmony_ci * initialized to default_chartab and user selectable via 18862306a36Sopenharmony_ci * /sys/module/speakup/parameters/chartab 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ciu_short spk_chartab[256]; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic u_short default_chartab[256] = { 19362306a36Sopenharmony_ci B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */ 19462306a36Sopenharmony_ci B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */ 19562306a36Sopenharmony_ci B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */ 19662306a36Sopenharmony_ci B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */ 19762306a36Sopenharmony_ci WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */ 19862306a36Sopenharmony_ci PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */ 19962306a36Sopenharmony_ci NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */ 20062306a36Sopenharmony_ci NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */ 20162306a36Sopenharmony_ci PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */ 20262306a36Sopenharmony_ci A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */ 20362306a36Sopenharmony_ci A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */ 20462306a36Sopenharmony_ci A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */ 20562306a36Sopenharmony_ci PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */ 20662306a36Sopenharmony_ci ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */ 20762306a36Sopenharmony_ci ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */ 20862306a36Sopenharmony_ci ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */ 20962306a36Sopenharmony_ci B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */ 21062306a36Sopenharmony_ci B_SYM, /* 135 */ 21162306a36Sopenharmony_ci B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */ 21262306a36Sopenharmony_ci B_CAPSYM, /* 143 */ 21362306a36Sopenharmony_ci B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */ 21462306a36Sopenharmony_ci B_SYM, /* 151 */ 21562306a36Sopenharmony_ci B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */ 21662306a36Sopenharmony_ci B_SYM, /* 159 */ 21762306a36Sopenharmony_ci WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */ 21862306a36Sopenharmony_ci B_SYM, /* 167 */ 21962306a36Sopenharmony_ci B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */ 22062306a36Sopenharmony_ci B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */ 22162306a36Sopenharmony_ci B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */ 22262306a36Sopenharmony_ci A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */ 22362306a36Sopenharmony_ci A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */ 22462306a36Sopenharmony_ci A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */ 22562306a36Sopenharmony_ci A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */ 22662306a36Sopenharmony_ci ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */ 22762306a36Sopenharmony_ci ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */ 22862306a36Sopenharmony_ci ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */ 22962306a36Sopenharmony_ci ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */ 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistruct task_struct *speakup_task; 23362306a36Sopenharmony_cistruct bleep spk_unprocessed_sound; 23462306a36Sopenharmony_cistatic int spk_keydown; 23562306a36Sopenharmony_cistatic u16 spk_lastkey; 23662306a36Sopenharmony_cistatic u_char spk_close_press, keymap_flags; 23762306a36Sopenharmony_cistatic u_char last_keycode, this_speakup_key; 23862306a36Sopenharmony_cistatic u_long last_spk_jiffy; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistruct st_spk_t *speakup_console[MAX_NR_CONSOLES]; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ciDEFINE_MUTEX(spk_mutex); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int keyboard_notifier_call(struct notifier_block *, 24562306a36Sopenharmony_ci unsigned long code, void *param); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic struct notifier_block keyboard_notifier_block = { 24862306a36Sopenharmony_ci .notifier_call = keyboard_notifier_call, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int vt_notifier_call(struct notifier_block *, 25262306a36Sopenharmony_ci unsigned long code, void *param); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic struct notifier_block vt_notifier_block = { 25562306a36Sopenharmony_ci .notifier_call = vt_notifier_call, 25662306a36Sopenharmony_ci}; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic unsigned char get_attributes(struct vc_data *vc, u16 *pos) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true); 26162306a36Sopenharmony_ci return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void speakup_date(struct vc_data *vc) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci spk_x = spk_cx = vc->state.x; 26762306a36Sopenharmony_ci spk_y = spk_cy = vc->state.y; 26862306a36Sopenharmony_ci spk_pos = spk_cp = vc->vc_pos; 26962306a36Sopenharmony_ci spk_old_attr = spk_attr; 27062306a36Sopenharmony_ci spk_attr = get_attributes(vc, (u_short *)spk_pos); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void bleep(u_short val) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci static const short vals[] = { 27662306a36Sopenharmony_ci 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659 27762306a36Sopenharmony_ci }; 27862306a36Sopenharmony_ci short freq; 27962306a36Sopenharmony_ci int time = spk_bleep_time; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci freq = vals[val % 12]; 28262306a36Sopenharmony_ci if (val > 11) 28362306a36Sopenharmony_ci freq *= (1 << (val / 12)); 28462306a36Sopenharmony_ci spk_unprocessed_sound.freq = freq; 28562306a36Sopenharmony_ci spk_unprocessed_sound.jiffies = msecs_to_jiffies(time); 28662306a36Sopenharmony_ci spk_unprocessed_sound.active = 1; 28762306a36Sopenharmony_ci /* We can only have 1 active sound at a time. */ 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void speakup_shut_up(struct vc_data *vc) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci if (spk_killed) 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci spk_shut_up |= 0x01; 29562306a36Sopenharmony_ci spk_parked &= 0xfe; 29662306a36Sopenharmony_ci speakup_date(vc); 29762306a36Sopenharmony_ci if (synth) 29862306a36Sopenharmony_ci spk_do_flush(); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic void speech_kill(struct vc_data *vc) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci char val = synth->is_alive(synth); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (val == 0) 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* re-enables synth, if disabled */ 30962306a36Sopenharmony_ci if (val == 2 || spk_killed) { 31062306a36Sopenharmony_ci /* dead */ 31162306a36Sopenharmony_ci spk_shut_up &= ~0x40; 31262306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE)); 31362306a36Sopenharmony_ci } else { 31462306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP)); 31562306a36Sopenharmony_ci spk_shut_up |= 0x40; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void speakup_off(struct vc_data *vc) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci if (spk_shut_up & 0x80) { 32262306a36Sopenharmony_ci spk_shut_up &= 0x7f; 32362306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER)); 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci spk_shut_up |= 0x80; 32662306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF)); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci speakup_date(vc); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void speakup_parked(struct vc_data *vc) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci if (spk_parked & 0x80) { 33462306a36Sopenharmony_ci spk_parked = 0; 33562306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_UNPARKED)); 33662306a36Sopenharmony_ci } else { 33762306a36Sopenharmony_ci spk_parked |= 0x80; 33862306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_PARKED)); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void speakup_cut(struct vc_data *vc) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci static const char err_buf[] = "set selection failed"; 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!mark_cut_flag) { 34862306a36Sopenharmony_ci mark_cut_flag = 1; 34962306a36Sopenharmony_ci spk_xs = (u_short)spk_x; 35062306a36Sopenharmony_ci spk_ys = (u_short)spk_y; 35162306a36Sopenharmony_ci spk_sel_cons = vc; 35262306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_MARK)); 35362306a36Sopenharmony_ci return; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci spk_xe = (u_short)spk_x; 35662306a36Sopenharmony_ci spk_ye = (u_short)spk_y; 35762306a36Sopenharmony_ci mark_cut_flag = 0; 35862306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_CUT)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ret = speakup_set_selection(tty); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci switch (ret) { 36362306a36Sopenharmony_ci case 0: 36462306a36Sopenharmony_ci break; /* no error */ 36562306a36Sopenharmony_ci case -EFAULT: 36662306a36Sopenharmony_ci pr_warn("%sEFAULT\n", err_buf); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case -EINVAL: 36962306a36Sopenharmony_ci pr_warn("%sEINVAL\n", err_buf); 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case -ENOMEM: 37262306a36Sopenharmony_ci pr_warn("%sENOMEM\n", err_buf); 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void speakup_paste(struct vc_data *vc) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci if (mark_cut_flag) { 38062306a36Sopenharmony_ci mark_cut_flag = 0; 38162306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED)); 38262306a36Sopenharmony_ci } else { 38362306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_PASTE)); 38462306a36Sopenharmony_ci speakup_paste_selection(tty); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void say_attributes(struct vc_data *vc) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int fg = spk_attr & 0x0f; 39162306a36Sopenharmony_ci int bg = spk_attr >> 4; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg)); 39462306a36Sopenharmony_ci if (bg > 7) { 39562306a36Sopenharmony_ci synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING)); 39662306a36Sopenharmony_ci bg -= 8; 39762306a36Sopenharmony_ci } else { 39862306a36Sopenharmony_ci synth_printf(" %s ", spk_msg_get(MSG_ON)); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg)); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* must be ordered same as edge_msgs in enum msg_index_t */ 40462306a36Sopenharmony_cienum edge { 40562306a36Sopenharmony_ci edge_none = 0, 40662306a36Sopenharmony_ci edge_top, 40762306a36Sopenharmony_ci edge_bottom, 40862306a36Sopenharmony_ci edge_left, 40962306a36Sopenharmony_ci edge_right, 41062306a36Sopenharmony_ci edge_quiet 41162306a36Sopenharmony_ci}; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic void announce_edge(struct vc_data *vc, enum edge msg_id) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci if (spk_bleeps & 1) 41662306a36Sopenharmony_ci bleep(spk_y); 41762306a36Sopenharmony_ci if ((spk_bleeps & 2) && (msg_id < edge_quiet)) 41862306a36Sopenharmony_ci synth_printf("%s\n", 41962306a36Sopenharmony_ci spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1)); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void speak_char(u16 ch) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci char *cp; 42562306a36Sopenharmony_ci struct var_t *direct = spk_get_var(DIRECT); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (ch >= 0x100 || (direct && direct->u.n.value)) { 42862306a36Sopenharmony_ci if (ch < 0x100 && IS_CHAR(ch, B_CAP)) { 42962306a36Sopenharmony_ci spk_pitch_shift++; 43062306a36Sopenharmony_ci synth_printf("%s", spk_str_caps_start); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci synth_putwc_s(ch); 43362306a36Sopenharmony_ci if (ch < 0x100 && IS_CHAR(ch, B_CAP)) 43462306a36Sopenharmony_ci synth_printf("%s", spk_str_caps_stop); 43562306a36Sopenharmony_ci return; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci cp = spk_characters[ch]; 43962306a36Sopenharmony_ci if (!cp) { 44062306a36Sopenharmony_ci pr_info("%s: cp == NULL!\n", __func__); 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci if (IS_CHAR(ch, B_CAP)) { 44462306a36Sopenharmony_ci spk_pitch_shift++; 44562306a36Sopenharmony_ci synth_printf("%s %s %s", 44662306a36Sopenharmony_ci spk_str_caps_start, cp, spk_str_caps_stop); 44762306a36Sopenharmony_ci } else { 44862306a36Sopenharmony_ci if (*cp == '^') { 44962306a36Sopenharmony_ci cp++; 45062306a36Sopenharmony_ci synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp); 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci synth_printf(" %s ", cp); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci u16 ch = ' '; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (vc && pos) { 46262306a36Sopenharmony_ci u16 w; 46362306a36Sopenharmony_ci u16 c; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true); 46662306a36Sopenharmony_ci w = scr_readw(pos); 46762306a36Sopenharmony_ci c = w & 0xff; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (w & vc->vc_hi_font_mask) { 47062306a36Sopenharmony_ci w &= ~vc->vc_hi_font_mask; 47162306a36Sopenharmony_ci c |= 0x100; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ch = inverse_translate(vc, c, true); 47562306a36Sopenharmony_ci *attribs = (w & 0xff00) >> 8; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci return ch; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void say_char(struct vc_data *vc) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci u16 ch; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci spk_old_attr = spk_attr; 48562306a36Sopenharmony_ci ch = get_char(vc, (u_short *)spk_pos, &spk_attr); 48662306a36Sopenharmony_ci if (spk_attr != spk_old_attr) { 48762306a36Sopenharmony_ci if (spk_attrib_bleep & 1) 48862306a36Sopenharmony_ci bleep(spk_y); 48962306a36Sopenharmony_ci if (spk_attrib_bleep & 2) 49062306a36Sopenharmony_ci say_attributes(vc); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci speak_char(ch); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void say_phonetic_char(struct vc_data *vc) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci u16 ch; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci spk_old_attr = spk_attr; 50062306a36Sopenharmony_ci ch = get_char(vc, (u_short *)spk_pos, &spk_attr); 50162306a36Sopenharmony_ci if (ch <= 0x7f && isalpha(ch)) { 50262306a36Sopenharmony_ci ch &= 0x1f; 50362306a36Sopenharmony_ci synth_printf("%s\n", phonetic[--ch]); 50462306a36Sopenharmony_ci } else { 50562306a36Sopenharmony_ci if (ch < 0x100 && IS_CHAR(ch, B_NUM)) 50662306a36Sopenharmony_ci synth_printf("%s ", spk_msg_get(MSG_NUMBER)); 50762306a36Sopenharmony_ci speak_char(ch); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void say_prev_char(struct vc_data *vc) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci spk_parked |= 0x01; 51462306a36Sopenharmony_ci if (spk_x == 0) { 51562306a36Sopenharmony_ci announce_edge(vc, edge_left); 51662306a36Sopenharmony_ci return; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci spk_x--; 51962306a36Sopenharmony_ci spk_pos -= 2; 52062306a36Sopenharmony_ci say_char(vc); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void say_next_char(struct vc_data *vc) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci spk_parked |= 0x01; 52662306a36Sopenharmony_ci if (spk_x == vc->vc_cols - 1) { 52762306a36Sopenharmony_ci announce_edge(vc, edge_right); 52862306a36Sopenharmony_ci return; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci spk_x++; 53162306a36Sopenharmony_ci spk_pos += 2; 53262306a36Sopenharmony_ci say_char(vc); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* get_word - will first check to see if the character under the 53662306a36Sopenharmony_ci * reading cursor is a space and if spk_say_word_ctl is true it will 53762306a36Sopenharmony_ci * return the word space. If spk_say_word_ctl is not set it will check to 53862306a36Sopenharmony_ci * see if there is a word starting on the next position to the right 53962306a36Sopenharmony_ci * and return that word if it exists. If it does not exist it will 54062306a36Sopenharmony_ci * move left to the beginning of any previous word on the line or the 54162306a36Sopenharmony_ci * beginning off the line whichever comes first.. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic u_long get_word(struct vc_data *vc) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos; 54762306a36Sopenharmony_ci u16 ch; 54862306a36Sopenharmony_ci u16 attr_ch; 54962306a36Sopenharmony_ci u_char temp; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci spk_old_attr = spk_attr; 55262306a36Sopenharmony_ci ch = get_char(vc, (u_short *)tmp_pos, &temp); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* decided to take out the sayword if on a space (mis-information */ 55562306a36Sopenharmony_ci if (spk_say_word_ctl && ch == SPACE) { 55662306a36Sopenharmony_ci *buf = '\0'; 55762306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_SPACE)); 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci } else if (tmpx < vc->vc_cols - 2 && 56062306a36Sopenharmony_ci (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) && 56162306a36Sopenharmony_ci get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) { 56262306a36Sopenharmony_ci tmp_pos += 2; 56362306a36Sopenharmony_ci tmpx++; 56462306a36Sopenharmony_ci } else { 56562306a36Sopenharmony_ci while (tmpx > 0) { 56662306a36Sopenharmony_ci ch = get_char(vc, (u_short *)tmp_pos - 1, &temp); 56762306a36Sopenharmony_ci if ((ch == SPACE || ch == 0 || 56862306a36Sopenharmony_ci (ch < 0x100 && IS_WDLM(ch))) && 56962306a36Sopenharmony_ci get_char(vc, (u_short *)tmp_pos, &temp) > SPACE) 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci tmp_pos -= 2; 57262306a36Sopenharmony_ci tmpx--; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); 57662306a36Sopenharmony_ci buf[cnt++] = attr_ch; 57762306a36Sopenharmony_ci while (tmpx < vc->vc_cols - 1) { 57862306a36Sopenharmony_ci tmp_pos += 2; 57962306a36Sopenharmony_ci tmpx++; 58062306a36Sopenharmony_ci ch = get_char(vc, (u_short *)tmp_pos, &temp); 58162306a36Sopenharmony_ci if (ch == SPACE || ch == 0 || 58262306a36Sopenharmony_ci (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) && 58362306a36Sopenharmony_ci ch > SPACE)) 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci buf[cnt++] = ch; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci buf[cnt] = '\0'; 58862306a36Sopenharmony_ci return cnt; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void say_word(struct vc_data *vc) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci u_long cnt = get_word(vc); 59462306a36Sopenharmony_ci u_short saved_punc_mask = spk_punc_mask; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (cnt == 0) 59762306a36Sopenharmony_ci return; 59862306a36Sopenharmony_ci spk_punc_mask = PUNC; 59962306a36Sopenharmony_ci buf[cnt++] = SPACE; 60062306a36Sopenharmony_ci spkup_write(buf, cnt); 60162306a36Sopenharmony_ci spk_punc_mask = saved_punc_mask; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void say_prev_word(struct vc_data *vc) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci u_char temp; 60762306a36Sopenharmony_ci u16 ch; 60862306a36Sopenharmony_ci enum edge edge_said = edge_none; 60962306a36Sopenharmony_ci u_short last_state = 0, state = 0; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci spk_parked |= 0x01; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (spk_x == 0) { 61462306a36Sopenharmony_ci if (spk_y == 0) { 61562306a36Sopenharmony_ci announce_edge(vc, edge_top); 61662306a36Sopenharmony_ci return; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci spk_y--; 61962306a36Sopenharmony_ci spk_x = vc->vc_cols; 62062306a36Sopenharmony_ci edge_said = edge_quiet; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci while (1) { 62362306a36Sopenharmony_ci if (spk_x == 0) { 62462306a36Sopenharmony_ci if (spk_y == 0) { 62562306a36Sopenharmony_ci edge_said = edge_top; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci if (edge_said != edge_quiet) 62962306a36Sopenharmony_ci edge_said = edge_left; 63062306a36Sopenharmony_ci if (state > 0) 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci spk_y--; 63362306a36Sopenharmony_ci spk_x = vc->vc_cols - 1; 63462306a36Sopenharmony_ci } else { 63562306a36Sopenharmony_ci spk_x--; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci spk_pos -= 2; 63862306a36Sopenharmony_ci ch = get_char(vc, (u_short *)spk_pos, &temp); 63962306a36Sopenharmony_ci if (ch == SPACE || ch == 0) 64062306a36Sopenharmony_ci state = 0; 64162306a36Sopenharmony_ci else if (ch < 0x100 && IS_WDLM(ch)) 64262306a36Sopenharmony_ci state = 1; 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci state = 2; 64562306a36Sopenharmony_ci if (state < last_state) { 64662306a36Sopenharmony_ci spk_pos += 2; 64762306a36Sopenharmony_ci spk_x++; 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci last_state = state; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci if (spk_x == 0 && edge_said == edge_quiet) 65362306a36Sopenharmony_ci edge_said = edge_left; 65462306a36Sopenharmony_ci if (edge_said > edge_none && edge_said < edge_quiet) 65562306a36Sopenharmony_ci announce_edge(vc, edge_said); 65662306a36Sopenharmony_ci say_word(vc); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void say_next_word(struct vc_data *vc) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci u_char temp; 66262306a36Sopenharmony_ci u16 ch; 66362306a36Sopenharmony_ci enum edge edge_said = edge_none; 66462306a36Sopenharmony_ci u_short last_state = 2, state = 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci spk_parked |= 0x01; 66762306a36Sopenharmony_ci if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) { 66862306a36Sopenharmony_ci announce_edge(vc, edge_bottom); 66962306a36Sopenharmony_ci return; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci while (1) { 67262306a36Sopenharmony_ci ch = get_char(vc, (u_short *)spk_pos, &temp); 67362306a36Sopenharmony_ci if (ch == SPACE || ch == 0) 67462306a36Sopenharmony_ci state = 0; 67562306a36Sopenharmony_ci else if (ch < 0x100 && IS_WDLM(ch)) 67662306a36Sopenharmony_ci state = 1; 67762306a36Sopenharmony_ci else 67862306a36Sopenharmony_ci state = 2; 67962306a36Sopenharmony_ci if (state > last_state) 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci if (spk_x >= vc->vc_cols - 1) { 68262306a36Sopenharmony_ci if (spk_y == vc->vc_rows - 1) { 68362306a36Sopenharmony_ci edge_said = edge_bottom; 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci state = 0; 68762306a36Sopenharmony_ci spk_y++; 68862306a36Sopenharmony_ci spk_x = 0; 68962306a36Sopenharmony_ci edge_said = edge_right; 69062306a36Sopenharmony_ci } else { 69162306a36Sopenharmony_ci spk_x++; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci spk_pos += 2; 69462306a36Sopenharmony_ci last_state = state; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci if (edge_said > edge_none) 69762306a36Sopenharmony_ci announce_edge(vc, edge_said); 69862306a36Sopenharmony_ci say_word(vc); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic void spell_word(struct vc_data *vc) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci static char const *delay_str[] = { "", ",", ".", ". .", ". . ." }; 70462306a36Sopenharmony_ci u16 *cp = buf; 70562306a36Sopenharmony_ci char *cp1; 70662306a36Sopenharmony_ci char *str_cap = spk_str_caps_stop; 70762306a36Sopenharmony_ci char *last_cap = spk_str_caps_stop; 70862306a36Sopenharmony_ci struct var_t *direct = spk_get_var(DIRECT); 70962306a36Sopenharmony_ci u16 ch; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (!get_word(vc)) 71262306a36Sopenharmony_ci return; 71362306a36Sopenharmony_ci while ((ch = *cp)) { 71462306a36Sopenharmony_ci if (cp != buf) 71562306a36Sopenharmony_ci synth_printf(" %s ", delay_str[spk_spell_delay]); 71662306a36Sopenharmony_ci /* FIXME: Non-latin1 considered as lower case */ 71762306a36Sopenharmony_ci if (ch < 0x100 && IS_CHAR(ch, B_CAP)) { 71862306a36Sopenharmony_ci str_cap = spk_str_caps_start; 71962306a36Sopenharmony_ci if (*spk_str_caps_stop) 72062306a36Sopenharmony_ci spk_pitch_shift++; 72162306a36Sopenharmony_ci else /* synth has no pitch */ 72262306a36Sopenharmony_ci last_cap = spk_str_caps_stop; 72362306a36Sopenharmony_ci } else { 72462306a36Sopenharmony_ci str_cap = spk_str_caps_stop; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci if (str_cap != last_cap) { 72762306a36Sopenharmony_ci synth_printf("%s", str_cap); 72862306a36Sopenharmony_ci last_cap = str_cap; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci if (ch >= 0x100 || (direct && direct->u.n.value)) { 73162306a36Sopenharmony_ci synth_putwc_s(ch); 73262306a36Sopenharmony_ci } else if (this_speakup_key == SPELL_PHONETIC && 73362306a36Sopenharmony_ci ch <= 0x7f && isalpha(ch)) { 73462306a36Sopenharmony_ci ch &= 0x1f; 73562306a36Sopenharmony_ci cp1 = phonetic[--ch]; 73662306a36Sopenharmony_ci synth_printf("%s", cp1); 73762306a36Sopenharmony_ci } else { 73862306a36Sopenharmony_ci cp1 = spk_characters[ch]; 73962306a36Sopenharmony_ci if (*cp1 == '^') { 74062306a36Sopenharmony_ci synth_printf("%s", spk_msg_get(MSG_CTRL)); 74162306a36Sopenharmony_ci cp1++; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci synth_printf("%s", cp1); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci cp++; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci if (str_cap != spk_str_caps_stop) 74862306a36Sopenharmony_ci synth_printf("%s", spk_str_caps_stop); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic int get_line(struct vc_data *vc) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci u_long tmp = spk_pos - (spk_x * 2); 75462306a36Sopenharmony_ci int i = 0; 75562306a36Sopenharmony_ci u_char tmp2; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci spk_old_attr = spk_attr; 75862306a36Sopenharmony_ci spk_attr = get_attributes(vc, (u_short *)spk_pos); 75962306a36Sopenharmony_ci for (i = 0; i < vc->vc_cols; i++) { 76062306a36Sopenharmony_ci buf[i] = get_char(vc, (u_short *)tmp, &tmp2); 76162306a36Sopenharmony_ci tmp += 2; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci for (--i; i >= 0; i--) 76462306a36Sopenharmony_ci if (buf[i] != SPACE) 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci return ++i; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void say_line(struct vc_data *vc) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci int i = get_line(vc); 77262306a36Sopenharmony_ci u16 *cp; 77362306a36Sopenharmony_ci u_short saved_punc_mask = spk_punc_mask; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (i == 0) { 77662306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_BLANK)); 77762306a36Sopenharmony_ci return; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci buf[i++] = '\n'; 78062306a36Sopenharmony_ci if (this_speakup_key == SAY_LINE_INDENT) { 78162306a36Sopenharmony_ci cp = buf; 78262306a36Sopenharmony_ci while (*cp == SPACE) 78362306a36Sopenharmony_ci cp++; 78462306a36Sopenharmony_ci synth_printf("%zd, ", (cp - buf) + 1); 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci spk_punc_mask = spk_punc_masks[spk_reading_punc]; 78762306a36Sopenharmony_ci spkup_write(buf, i); 78862306a36Sopenharmony_ci spk_punc_mask = saved_punc_mask; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic void say_prev_line(struct vc_data *vc) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci spk_parked |= 0x01; 79462306a36Sopenharmony_ci if (spk_y == 0) { 79562306a36Sopenharmony_ci announce_edge(vc, edge_top); 79662306a36Sopenharmony_ci return; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci spk_y--; 79962306a36Sopenharmony_ci spk_pos -= vc->vc_size_row; 80062306a36Sopenharmony_ci say_line(vc); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void say_next_line(struct vc_data *vc) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci spk_parked |= 0x01; 80662306a36Sopenharmony_ci if (spk_y == vc->vc_rows - 1) { 80762306a36Sopenharmony_ci announce_edge(vc, edge_bottom); 80862306a36Sopenharmony_ci return; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci spk_y++; 81162306a36Sopenharmony_ci spk_pos += vc->vc_size_row; 81262306a36Sopenharmony_ci say_line(vc); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic int say_from_to(struct vc_data *vc, u_long from, u_long to, 81662306a36Sopenharmony_ci int read_punc) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci int i = 0; 81962306a36Sopenharmony_ci u_char tmp; 82062306a36Sopenharmony_ci u_short saved_punc_mask = spk_punc_mask; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci spk_old_attr = spk_attr; 82362306a36Sopenharmony_ci spk_attr = get_attributes(vc, (u_short *)from); 82462306a36Sopenharmony_ci while (from < to) { 82562306a36Sopenharmony_ci buf[i++] = get_char(vc, (u_short *)from, &tmp); 82662306a36Sopenharmony_ci from += 2; 82762306a36Sopenharmony_ci if (i >= vc->vc_size_row) 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci for (--i; i >= 0; i--) 83162306a36Sopenharmony_ci if (buf[i] != SPACE) 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci buf[++i] = SPACE; 83462306a36Sopenharmony_ci buf[++i] = '\0'; 83562306a36Sopenharmony_ci if (i < 1) 83662306a36Sopenharmony_ci return i; 83762306a36Sopenharmony_ci if (read_punc) 83862306a36Sopenharmony_ci spk_punc_mask = spk_punc_info[spk_reading_punc].mask; 83962306a36Sopenharmony_ci spkup_write(buf, i); 84062306a36Sopenharmony_ci if (read_punc) 84162306a36Sopenharmony_ci spk_punc_mask = saved_punc_mask; 84262306a36Sopenharmony_ci return i - 1; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic void say_line_from_to(struct vc_data *vc, u_long from, u_long to, 84662306a36Sopenharmony_ci int read_punc) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci u_long start = vc->vc_origin + (spk_y * vc->vc_size_row); 84962306a36Sopenharmony_ci u_long end = start + (to * 2); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci start += from * 2; 85262306a36Sopenharmony_ci if (say_from_to(vc, start, end, read_punc) <= 0) 85362306a36Sopenharmony_ci if (cursor_track != read_all_mode) 85462306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_BLANK)); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci/* Sentence Reading Commands */ 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int currsentence; 86062306a36Sopenharmony_cistatic int numsentences[2]; 86162306a36Sopenharmony_cistatic u16 *sentbufend[2]; 86262306a36Sopenharmony_cistatic u16 *sentmarks[2][10]; 86362306a36Sopenharmony_cistatic int currbuf; 86462306a36Sopenharmony_cistatic int bn; 86562306a36Sopenharmony_cistatic u16 sentbuf[2][256]; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int say_sentence_num(int num, int prev) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci bn = currbuf; 87062306a36Sopenharmony_ci currsentence = num + 1; 87162306a36Sopenharmony_ci if (prev && --bn == -1) 87262306a36Sopenharmony_ci bn = 1; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (num > numsentences[bn]) 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]); 87862306a36Sopenharmony_ci return 1; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int get_sentence_buf(struct vc_data *vc, int read_punc) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci u_long start, end; 88462306a36Sopenharmony_ci int i, bn; 88562306a36Sopenharmony_ci u_char tmp; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci currbuf++; 88862306a36Sopenharmony_ci if (currbuf == 2) 88962306a36Sopenharmony_ci currbuf = 0; 89062306a36Sopenharmony_ci bn = currbuf; 89162306a36Sopenharmony_ci start = vc->vc_origin + ((spk_y) * vc->vc_size_row); 89262306a36Sopenharmony_ci end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci numsentences[bn] = 0; 89562306a36Sopenharmony_ci sentmarks[bn][0] = &sentbuf[bn][0]; 89662306a36Sopenharmony_ci i = 0; 89762306a36Sopenharmony_ci spk_old_attr = spk_attr; 89862306a36Sopenharmony_ci spk_attr = get_attributes(vc, (u_short *)start); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci while (start < end) { 90162306a36Sopenharmony_ci sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp); 90262306a36Sopenharmony_ci if (i > 0) { 90362306a36Sopenharmony_ci if (sentbuf[bn][i] == SPACE && 90462306a36Sopenharmony_ci sentbuf[bn][i - 1] == '.' && 90562306a36Sopenharmony_ci numsentences[bn] < 9) { 90662306a36Sopenharmony_ci /* Sentence Marker */ 90762306a36Sopenharmony_ci numsentences[bn]++; 90862306a36Sopenharmony_ci sentmarks[bn][numsentences[bn]] = 90962306a36Sopenharmony_ci &sentbuf[bn][i]; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci i++; 91362306a36Sopenharmony_ci start += 2; 91462306a36Sopenharmony_ci if (i >= vc->vc_size_row) 91562306a36Sopenharmony_ci break; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci for (--i; i >= 0; i--) 91962306a36Sopenharmony_ci if (sentbuf[bn][i] != SPACE) 92062306a36Sopenharmony_ci break; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (i < 1) 92362306a36Sopenharmony_ci return -1; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci sentbuf[bn][++i] = SPACE; 92662306a36Sopenharmony_ci sentbuf[bn][++i] = '\0'; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci sentbufend[bn] = &sentbuf[bn][i]; 92962306a36Sopenharmony_ci return numsentences[bn]; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void say_screen_from_to(struct vc_data *vc, u_long from, u_long to) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci u_long start = vc->vc_origin, end; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (from > 0) 93762306a36Sopenharmony_ci start += from * vc->vc_size_row; 93862306a36Sopenharmony_ci if (to > vc->vc_rows) 93962306a36Sopenharmony_ci to = vc->vc_rows; 94062306a36Sopenharmony_ci end = vc->vc_origin + (to * vc->vc_size_row); 94162306a36Sopenharmony_ci for (from = start; from < end; from = to) { 94262306a36Sopenharmony_ci to = from + vc->vc_size_row; 94362306a36Sopenharmony_ci say_from_to(vc, from, to, 1); 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic void say_screen(struct vc_data *vc) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci say_screen_from_to(vc, 0, vc->vc_rows); 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic void speakup_win_say(struct vc_data *vc) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci u_long start, end, from, to; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (win_start < 2) { 95762306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW)); 95862306a36Sopenharmony_ci return; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci start = vc->vc_origin + (win_top * vc->vc_size_row); 96162306a36Sopenharmony_ci end = vc->vc_origin + (win_bottom * vc->vc_size_row); 96262306a36Sopenharmony_ci while (start <= end) { 96362306a36Sopenharmony_ci from = start + (win_left * 2); 96462306a36Sopenharmony_ci to = start + (win_right * 2); 96562306a36Sopenharmony_ci say_from_to(vc, from, to, 1); 96662306a36Sopenharmony_ci start += vc->vc_size_row; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic void top_edge(struct vc_data *vc) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci spk_parked |= 0x01; 97362306a36Sopenharmony_ci spk_pos = vc->vc_origin + 2 * spk_x; 97462306a36Sopenharmony_ci spk_y = 0; 97562306a36Sopenharmony_ci say_line(vc); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic void bottom_edge(struct vc_data *vc) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci spk_parked |= 0x01; 98162306a36Sopenharmony_ci spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row; 98262306a36Sopenharmony_ci spk_y = vc->vc_rows - 1; 98362306a36Sopenharmony_ci say_line(vc); 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic void left_edge(struct vc_data *vc) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci spk_parked |= 0x01; 98962306a36Sopenharmony_ci spk_pos -= spk_x * 2; 99062306a36Sopenharmony_ci spk_x = 0; 99162306a36Sopenharmony_ci say_char(vc); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic void right_edge(struct vc_data *vc) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci spk_parked |= 0x01; 99762306a36Sopenharmony_ci spk_pos += (vc->vc_cols - spk_x - 1) * 2; 99862306a36Sopenharmony_ci spk_x = vc->vc_cols - 1; 99962306a36Sopenharmony_ci say_char(vc); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic void say_first_char(struct vc_data *vc) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci int i, len = get_line(vc); 100562306a36Sopenharmony_ci u16 ch; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci spk_parked |= 0x01; 100862306a36Sopenharmony_ci if (len == 0) { 100962306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_BLANK)); 101062306a36Sopenharmony_ci return; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci for (i = 0; i < len; i++) 101362306a36Sopenharmony_ci if (buf[i] != SPACE) 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci ch = buf[i]; 101662306a36Sopenharmony_ci spk_pos -= (spk_x - i) * 2; 101762306a36Sopenharmony_ci spk_x = i; 101862306a36Sopenharmony_ci synth_printf("%d, ", ++i); 101962306a36Sopenharmony_ci speak_char(ch); 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic void say_last_char(struct vc_data *vc) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci int len = get_line(vc); 102562306a36Sopenharmony_ci u16 ch; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci spk_parked |= 0x01; 102862306a36Sopenharmony_ci if (len == 0) { 102962306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_BLANK)); 103062306a36Sopenharmony_ci return; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci ch = buf[--len]; 103362306a36Sopenharmony_ci spk_pos -= (spk_x - len) * 2; 103462306a36Sopenharmony_ci spk_x = len; 103562306a36Sopenharmony_ci synth_printf("%d, ", ++len); 103662306a36Sopenharmony_ci speak_char(ch); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic void say_position(struct vc_data *vc) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1, 104262306a36Sopenharmony_ci vc->vc_num + 1); 104362306a36Sopenharmony_ci synth_printf("\n"); 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci/* Added by brianb */ 104762306a36Sopenharmony_cistatic void say_char_num(struct vc_data *vc) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci u_char tmp; 105062306a36Sopenharmony_ci u16 ch = get_char(vc, (u_short *)spk_pos, &tmp); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci/* these are stub functions to keep keyboard.c happy. */ 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic void say_from_top(struct vc_data *vc) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci say_screen_from_to(vc, 0, spk_y); 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic void say_to_bottom(struct vc_data *vc) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci say_screen_from_to(vc, spk_y, vc->vc_rows); 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic void say_from_left(struct vc_data *vc) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci say_line_from_to(vc, 0, spk_x, 1); 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic void say_to_right(struct vc_data *vc) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci say_line_from_to(vc, spk_x, vc->vc_cols, 1); 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci/* end of stub functions. */ 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic void spkup_write(const u16 *in_buf, int count) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci static int rep_count; 108262306a36Sopenharmony_ci static u16 ch = '\0', old_ch = '\0'; 108362306a36Sopenharmony_ci static u_short char_type, last_type; 108462306a36Sopenharmony_ci int in_count = count; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci spk_keydown = 0; 108762306a36Sopenharmony_ci while (count--) { 108862306a36Sopenharmony_ci if (cursor_track == read_all_mode) { 108962306a36Sopenharmony_ci /* Insert Sentence Index */ 109062306a36Sopenharmony_ci if ((in_buf == sentmarks[bn][currsentence]) && 109162306a36Sopenharmony_ci (currsentence <= numsentences[bn])) 109262306a36Sopenharmony_ci synth_insert_next_index(currsentence++); 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci ch = *in_buf++; 109562306a36Sopenharmony_ci if (ch < 0x100) 109662306a36Sopenharmony_ci char_type = spk_chartab[ch]; 109762306a36Sopenharmony_ci else 109862306a36Sopenharmony_ci char_type = ALPHA; 109962306a36Sopenharmony_ci if (ch == old_ch && !(char_type & B_NUM)) { 110062306a36Sopenharmony_ci if (++rep_count > 2) 110162306a36Sopenharmony_ci continue; 110262306a36Sopenharmony_ci } else { 110362306a36Sopenharmony_ci if ((last_type & CH_RPT) && rep_count > 2) { 110462306a36Sopenharmony_ci synth_printf(" "); 110562306a36Sopenharmony_ci synth_printf(spk_msg_get(MSG_REPEAT_DESC), 110662306a36Sopenharmony_ci ++rep_count); 110762306a36Sopenharmony_ci synth_printf(" "); 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci rep_count = 0; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci if (ch == spk_lastkey) { 111262306a36Sopenharmony_ci rep_count = 0; 111362306a36Sopenharmony_ci if (spk_key_echo == 1 && ch >= MINECHOCHAR) 111462306a36Sopenharmony_ci speak_char(ch); 111562306a36Sopenharmony_ci } else if (char_type & B_ALPHA) { 111662306a36Sopenharmony_ci if ((synth_flags & SF_DEC) && (last_type & PUNC)) 111762306a36Sopenharmony_ci synth_buffer_add(SPACE); 111862306a36Sopenharmony_ci synth_putwc_s(ch); 111962306a36Sopenharmony_ci } else if (char_type & B_NUM) { 112062306a36Sopenharmony_ci rep_count = 0; 112162306a36Sopenharmony_ci synth_putwc_s(ch); 112262306a36Sopenharmony_ci } else if (char_type & spk_punc_mask) { 112362306a36Sopenharmony_ci speak_char(ch); 112462306a36Sopenharmony_ci char_type &= ~PUNC; /* for dec nospell processing */ 112562306a36Sopenharmony_ci } else if (char_type & SYNTH_OK) { 112662306a36Sopenharmony_ci /* these are usually puncts like . and , which synth 112762306a36Sopenharmony_ci * needs for expression. 112862306a36Sopenharmony_ci * suppress multiple to get rid of long pauses and 112962306a36Sopenharmony_ci * clear repeat count 113062306a36Sopenharmony_ci * so if someone has 113162306a36Sopenharmony_ci * repeats on you don't get nothing repeated count 113262306a36Sopenharmony_ci */ 113362306a36Sopenharmony_ci if (ch != old_ch) 113462306a36Sopenharmony_ci synth_putwc_s(ch); 113562306a36Sopenharmony_ci else 113662306a36Sopenharmony_ci rep_count = 0; 113762306a36Sopenharmony_ci } else { 113862306a36Sopenharmony_ci/* send space and record position, if next is num overwrite space */ 113962306a36Sopenharmony_ci if (old_ch != ch) 114062306a36Sopenharmony_ci synth_buffer_add(SPACE); 114162306a36Sopenharmony_ci else 114262306a36Sopenharmony_ci rep_count = 0; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci old_ch = ch; 114562306a36Sopenharmony_ci last_type = char_type; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci spk_lastkey = 0; 114862306a36Sopenharmony_ci if (in_count > 2 && rep_count > 2) { 114962306a36Sopenharmony_ci if (last_type & CH_RPT) { 115062306a36Sopenharmony_ci synth_printf(" "); 115162306a36Sopenharmony_ci synth_printf(spk_msg_get(MSG_REPEAT_DESC2), 115262306a36Sopenharmony_ci ++rep_count); 115362306a36Sopenharmony_ci synth_printf(" "); 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci rep_count = 0; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic void read_all_doc(struct vc_data *vc); 116262306a36Sopenharmony_cistatic void cursor_done(struct timer_list *unused); 116362306a36Sopenharmony_cistatic DEFINE_TIMER(cursor_timer, cursor_done); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic void do_handle_shift(struct vc_data *vc, u_char value, char up_flag) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci unsigned long flags; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (!synth || up_flag || spk_killed) 117062306a36Sopenharmony_ci return; 117162306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 117262306a36Sopenharmony_ci if (cursor_track == read_all_mode) { 117362306a36Sopenharmony_ci switch (value) { 117462306a36Sopenharmony_ci case KVAL(K_SHIFT): 117562306a36Sopenharmony_ci del_timer(&cursor_timer); 117662306a36Sopenharmony_ci spk_shut_up &= 0xfe; 117762306a36Sopenharmony_ci spk_do_flush(); 117862306a36Sopenharmony_ci read_all_doc(vc); 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci case KVAL(K_CTRL): 118162306a36Sopenharmony_ci del_timer(&cursor_timer); 118262306a36Sopenharmony_ci cursor_track = prev_cursor_track; 118362306a36Sopenharmony_ci spk_shut_up &= 0xfe; 118462306a36Sopenharmony_ci spk_do_flush(); 118562306a36Sopenharmony_ci break; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci } else { 118862306a36Sopenharmony_ci spk_shut_up &= 0xfe; 118962306a36Sopenharmony_ci spk_do_flush(); 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci if (spk_say_ctrl && value < NUM_CTL_LABELS) 119262306a36Sopenharmony_ci synth_printf("%s", spk_msg_get(MSG_CTL_START + value)); 119362306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic void do_handle_latin(struct vc_data *vc, u_char value, char up_flag) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci unsigned long flags; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 120162306a36Sopenharmony_ci if (up_flag) { 120262306a36Sopenharmony_ci spk_lastkey = 0; 120362306a36Sopenharmony_ci spk_keydown = 0; 120462306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 120562306a36Sopenharmony_ci return; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci if (!synth || spk_killed) { 120862306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 120962306a36Sopenharmony_ci return; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci spk_shut_up &= 0xfe; 121262306a36Sopenharmony_ci spk_lastkey = value; 121362306a36Sopenharmony_ci spk_keydown++; 121462306a36Sopenharmony_ci spk_parked &= 0xfe; 121562306a36Sopenharmony_ci if (spk_key_echo == 2 && value >= MINECHOCHAR) 121662306a36Sopenharmony_ci speak_char(value); 121762306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ciint spk_set_key_info(const u_char *key_info, u_char *k_buffer) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci int i = 0, states, key_data_len; 122362306a36Sopenharmony_ci const u_char *cp = key_info; 122462306a36Sopenharmony_ci u_char *cp1 = k_buffer; 122562306a36Sopenharmony_ci u_char ch, version, num_keys; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci version = *cp++; 122862306a36Sopenharmony_ci if (version != KEY_MAP_VER) { 122962306a36Sopenharmony_ci pr_debug("version found %d should be %d\n", 123062306a36Sopenharmony_ci version, KEY_MAP_VER); 123162306a36Sopenharmony_ci return -EINVAL; 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci num_keys = *cp; 123462306a36Sopenharmony_ci states = (int)cp[1]; 123562306a36Sopenharmony_ci key_data_len = (states + 1) * (num_keys + 1); 123662306a36Sopenharmony_ci if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) { 123762306a36Sopenharmony_ci pr_debug("too many key_infos (%d over %u)\n", 123862306a36Sopenharmony_ci key_data_len + SHIFT_TBL_SIZE + 4, 123962306a36Sopenharmony_ci (unsigned int)(sizeof(spk_key_buf))); 124062306a36Sopenharmony_ci return -EINVAL; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci memset(k_buffer, 0, SHIFT_TBL_SIZE); 124362306a36Sopenharmony_ci memset(spk_our_keys, 0, sizeof(spk_our_keys)); 124462306a36Sopenharmony_ci spk_shift_table = k_buffer; 124562306a36Sopenharmony_ci spk_our_keys[0] = spk_shift_table; 124662306a36Sopenharmony_ci cp1 += SHIFT_TBL_SIZE; 124762306a36Sopenharmony_ci memcpy(cp1, cp, key_data_len + 3); 124862306a36Sopenharmony_ci /* get num_keys, states and data */ 124962306a36Sopenharmony_ci cp1 += 2; /* now pointing at shift states */ 125062306a36Sopenharmony_ci for (i = 1; i <= states; i++) { 125162306a36Sopenharmony_ci ch = *cp1++; 125262306a36Sopenharmony_ci if (ch >= SHIFT_TBL_SIZE) { 125362306a36Sopenharmony_ci pr_debug("(%d) not valid shift state (max_allowed = %d)\n", 125462306a36Sopenharmony_ci ch, SHIFT_TBL_SIZE); 125562306a36Sopenharmony_ci return -EINVAL; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci spk_shift_table[ch] = i; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci keymap_flags = *cp1++; 126062306a36Sopenharmony_ci while ((ch = *cp1)) { 126162306a36Sopenharmony_ci if (ch >= MAX_KEY) { 126262306a36Sopenharmony_ci pr_debug("(%d), not valid key, (max_allowed = %d)\n", 126362306a36Sopenharmony_ci ch, MAX_KEY); 126462306a36Sopenharmony_ci return -EINVAL; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci spk_our_keys[ch] = cp1; 126762306a36Sopenharmony_ci cp1 += states + 1; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci return 0; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cienum spk_vars_id { 127362306a36Sopenharmony_ci BELL_POS_ID = 0, SPELL_DELAY_ID, ATTRIB_BLEEP_ID, 127462306a36Sopenharmony_ci BLEEPS_ID, BLEEP_TIME_ID, PUNC_LEVEL_ID, 127562306a36Sopenharmony_ci READING_PUNC_ID, CURSOR_TIME_ID, SAY_CONTROL_ID, 127662306a36Sopenharmony_ci SAY_WORD_CTL_ID, NO_INTERRUPT_ID, KEY_ECHO_ID, 127762306a36Sopenharmony_ci CUR_PHONETIC_ID, V_LAST_VAR_ID, NB_ID 127862306a36Sopenharmony_ci}; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic struct var_t spk_vars[NB_ID] = { 128162306a36Sopenharmony_ci /* bell must be first to set high limit */ 128262306a36Sopenharmony_ci [BELL_POS_ID] = { BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} }, 128362306a36Sopenharmony_ci [SPELL_DELAY_ID] = { SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} }, 128462306a36Sopenharmony_ci [ATTRIB_BLEEP_ID] = { ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} }, 128562306a36Sopenharmony_ci [BLEEPS_ID] = { BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} }, 128662306a36Sopenharmony_ci [BLEEP_TIME_ID] = { BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} }, 128762306a36Sopenharmony_ci [PUNC_LEVEL_ID] = { PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} }, 128862306a36Sopenharmony_ci [READING_PUNC_ID] = { READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} }, 128962306a36Sopenharmony_ci [CURSOR_TIME_ID] = { CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} }, 129062306a36Sopenharmony_ci [SAY_CONTROL_ID] = { SAY_CONTROL, TOGGLE_0}, 129162306a36Sopenharmony_ci [SAY_WORD_CTL_ID] = {SAY_WORD_CTL, TOGGLE_0}, 129262306a36Sopenharmony_ci [NO_INTERRUPT_ID] = { NO_INTERRUPT, TOGGLE_0}, 129362306a36Sopenharmony_ci [KEY_ECHO_ID] = { KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} }, 129462306a36Sopenharmony_ci [CUR_PHONETIC_ID] = { CUR_PHONETIC, .u.n = {NULL, 0, 0, 1, 0, 0, NULL} }, 129562306a36Sopenharmony_ci V_LAST_VAR 129662306a36Sopenharmony_ci}; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic void toggle_cursoring(struct vc_data *vc) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci if (cursor_track == read_all_mode) 130162306a36Sopenharmony_ci cursor_track = prev_cursor_track; 130262306a36Sopenharmony_ci if (++cursor_track >= CT_Max) 130362306a36Sopenharmony_ci cursor_track = 0; 130462306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track)); 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_civoid spk_reset_default_chars(void) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci int i; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci /* First, free any non-default */ 131262306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 131362306a36Sopenharmony_ci if (spk_characters[i] && 131462306a36Sopenharmony_ci (spk_characters[i] != spk_default_chars[i])) 131562306a36Sopenharmony_ci kfree(spk_characters[i]); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars)); 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_civoid spk_reset_default_chartab(void) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci memcpy(spk_chartab, default_chartab, sizeof(default_chartab)); 132462306a36Sopenharmony_ci} 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic const struct st_bits_data *pb_edit; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_cistatic int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci short mask = pb_edit->mask, ch_type = spk_chartab[ch]; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE) 133362306a36Sopenharmony_ci return -1; 133462306a36Sopenharmony_ci if (ch == SPACE) { 133562306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE)); 133662306a36Sopenharmony_ci spk_special_handler = NULL; 133762306a36Sopenharmony_ci return 1; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci if (mask < PUNC && !(ch_type & PUNC)) 134062306a36Sopenharmony_ci return -1; 134162306a36Sopenharmony_ci spk_chartab[ch] ^= mask; 134262306a36Sopenharmony_ci speak_char(ch); 134362306a36Sopenharmony_ci synth_printf(" %s\n", 134462306a36Sopenharmony_ci (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) : 134562306a36Sopenharmony_ci spk_msg_get(MSG_OFF)); 134662306a36Sopenharmony_ci return 1; 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci/* Allocation concurrency is protected by the console semaphore */ 135062306a36Sopenharmony_cistatic int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci int vc_num; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci vc_num = vc->vc_num; 135562306a36Sopenharmony_ci if (!speakup_console[vc_num]) { 135662306a36Sopenharmony_ci speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]), 135762306a36Sopenharmony_ci gfp_flags); 135862306a36Sopenharmony_ci if (!speakup_console[vc_num]) 135962306a36Sopenharmony_ci return -ENOMEM; 136062306a36Sopenharmony_ci speakup_date(vc); 136162306a36Sopenharmony_ci } else if (!spk_parked) { 136262306a36Sopenharmony_ci speakup_date(vc); 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci return 0; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic void speakup_deallocate(struct vc_data *vc) 136962306a36Sopenharmony_ci{ 137062306a36Sopenharmony_ci int vc_num; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci vc_num = vc->vc_num; 137362306a36Sopenharmony_ci kfree(speakup_console[vc_num]); 137462306a36Sopenharmony_ci speakup_console[vc_num] = NULL; 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cienum read_all_command { 137862306a36Sopenharmony_ci RA_NEXT_SENT = KVAL(K_DOWN)+1, 137962306a36Sopenharmony_ci RA_PREV_LINE = KVAL(K_LEFT)+1, 138062306a36Sopenharmony_ci RA_NEXT_LINE = KVAL(K_RIGHT)+1, 138162306a36Sopenharmony_ci RA_PREV_SENT = KVAL(K_UP)+1, 138262306a36Sopenharmony_ci RA_DOWN_ARROW, 138362306a36Sopenharmony_ci RA_TIMER, 138462306a36Sopenharmony_ci RA_FIND_NEXT_SENT, 138562306a36Sopenharmony_ci RA_FIND_PREV_SENT, 138662306a36Sopenharmony_ci}; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic u_char is_cursor; 138962306a36Sopenharmony_cistatic u_long old_cursor_pos, old_cursor_x, old_cursor_y; 139062306a36Sopenharmony_cistatic int cursor_con; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cistatic void reset_highlight_buffers(struct vc_data *); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic enum read_all_command read_all_key; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_cistatic int in_keyboard_notifier; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_cistatic void start_read_all_timer(struct vc_data *vc, enum read_all_command command); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic void kbd_fakekey2(struct vc_data *vc, enum read_all_command command) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci del_timer(&cursor_timer); 140362306a36Sopenharmony_ci speakup_fake_down_arrow(); 140462306a36Sopenharmony_ci start_read_all_timer(vc, command); 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic void read_all_doc(struct vc_data *vc) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci if ((vc->vc_num != fg_console) || !synth || spk_shut_up) 141062306a36Sopenharmony_ci return; 141162306a36Sopenharmony_ci if (!synth_supports_indexing()) 141262306a36Sopenharmony_ci return; 141362306a36Sopenharmony_ci if (cursor_track != read_all_mode) 141462306a36Sopenharmony_ci prev_cursor_track = cursor_track; 141562306a36Sopenharmony_ci cursor_track = read_all_mode; 141662306a36Sopenharmony_ci spk_reset_index_count(0); 141762306a36Sopenharmony_ci if (get_sentence_buf(vc, 0) == -1) { 141862306a36Sopenharmony_ci del_timer(&cursor_timer); 141962306a36Sopenharmony_ci if (!in_keyboard_notifier) 142062306a36Sopenharmony_ci speakup_fake_down_arrow(); 142162306a36Sopenharmony_ci start_read_all_timer(vc, RA_DOWN_ARROW); 142262306a36Sopenharmony_ci } else { 142362306a36Sopenharmony_ci say_sentence_num(0, 0); 142462306a36Sopenharmony_ci synth_insert_next_index(0); 142562306a36Sopenharmony_ci start_read_all_timer(vc, RA_TIMER); 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_cistatic void stop_read_all(struct vc_data *vc) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci del_timer(&cursor_timer); 143262306a36Sopenharmony_ci cursor_track = prev_cursor_track; 143362306a36Sopenharmony_ci spk_shut_up &= 0xfe; 143462306a36Sopenharmony_ci spk_do_flush(); 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cistatic void start_read_all_timer(struct vc_data *vc, enum read_all_command command) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct var_t *cursor_timeout; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci cursor_con = vc->vc_num; 144262306a36Sopenharmony_ci read_all_key = command; 144362306a36Sopenharmony_ci cursor_timeout = spk_get_var(CURSOR_TIME); 144462306a36Sopenharmony_ci mod_timer(&cursor_timer, 144562306a36Sopenharmony_ci jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic void handle_cursor_read_all(struct vc_data *vc, enum read_all_command command) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci int indcount, sentcount, rv, sn; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci switch (command) { 145362306a36Sopenharmony_ci case RA_NEXT_SENT: 145462306a36Sopenharmony_ci /* Get Current Sentence */ 145562306a36Sopenharmony_ci spk_get_index_count(&indcount, &sentcount); 145662306a36Sopenharmony_ci /*printk("%d %d ", indcount, sentcount); */ 145762306a36Sopenharmony_ci spk_reset_index_count(sentcount + 1); 145862306a36Sopenharmony_ci if (indcount == 1) { 145962306a36Sopenharmony_ci if (!say_sentence_num(sentcount + 1, 0)) { 146062306a36Sopenharmony_ci kbd_fakekey2(vc, RA_FIND_NEXT_SENT); 146162306a36Sopenharmony_ci return; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci synth_insert_next_index(0); 146462306a36Sopenharmony_ci } else { 146562306a36Sopenharmony_ci sn = 0; 146662306a36Sopenharmony_ci if (!say_sentence_num(sentcount + 1, 1)) { 146762306a36Sopenharmony_ci sn = 1; 146862306a36Sopenharmony_ci spk_reset_index_count(sn); 146962306a36Sopenharmony_ci } else { 147062306a36Sopenharmony_ci synth_insert_next_index(0); 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci if (!say_sentence_num(sn, 0)) { 147362306a36Sopenharmony_ci kbd_fakekey2(vc, RA_FIND_NEXT_SENT); 147462306a36Sopenharmony_ci return; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci synth_insert_next_index(0); 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci start_read_all_timer(vc, RA_TIMER); 147962306a36Sopenharmony_ci break; 148062306a36Sopenharmony_ci case RA_PREV_SENT: 148162306a36Sopenharmony_ci break; 148262306a36Sopenharmony_ci case RA_NEXT_LINE: 148362306a36Sopenharmony_ci read_all_doc(vc); 148462306a36Sopenharmony_ci break; 148562306a36Sopenharmony_ci case RA_PREV_LINE: 148662306a36Sopenharmony_ci break; 148762306a36Sopenharmony_ci case RA_DOWN_ARROW: 148862306a36Sopenharmony_ci if (get_sentence_buf(vc, 0) == -1) { 148962306a36Sopenharmony_ci kbd_fakekey2(vc, RA_DOWN_ARROW); 149062306a36Sopenharmony_ci } else { 149162306a36Sopenharmony_ci say_sentence_num(0, 0); 149262306a36Sopenharmony_ci synth_insert_next_index(0); 149362306a36Sopenharmony_ci start_read_all_timer(vc, RA_TIMER); 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci break; 149662306a36Sopenharmony_ci case RA_FIND_NEXT_SENT: 149762306a36Sopenharmony_ci rv = get_sentence_buf(vc, 0); 149862306a36Sopenharmony_ci if (rv == -1) 149962306a36Sopenharmony_ci read_all_doc(vc); 150062306a36Sopenharmony_ci if (rv == 0) { 150162306a36Sopenharmony_ci kbd_fakekey2(vc, RA_FIND_NEXT_SENT); 150262306a36Sopenharmony_ci } else { 150362306a36Sopenharmony_ci say_sentence_num(1, 0); 150462306a36Sopenharmony_ci synth_insert_next_index(0); 150562306a36Sopenharmony_ci start_read_all_timer(vc, RA_TIMER); 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci break; 150862306a36Sopenharmony_ci case RA_FIND_PREV_SENT: 150962306a36Sopenharmony_ci break; 151062306a36Sopenharmony_ci case RA_TIMER: 151162306a36Sopenharmony_ci spk_get_index_count(&indcount, &sentcount); 151262306a36Sopenharmony_ci if (indcount < 2) 151362306a36Sopenharmony_ci kbd_fakekey2(vc, RA_DOWN_ARROW); 151462306a36Sopenharmony_ci else 151562306a36Sopenharmony_ci start_read_all_timer(vc, RA_TIMER); 151662306a36Sopenharmony_ci break; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_cistatic int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci unsigned long flags; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 152562306a36Sopenharmony_ci if (cursor_track == read_all_mode) { 152662306a36Sopenharmony_ci spk_parked &= 0xfe; 152762306a36Sopenharmony_ci if (!synth || up_flag || spk_shut_up) { 152862306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 152962306a36Sopenharmony_ci return NOTIFY_STOP; 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci del_timer(&cursor_timer); 153262306a36Sopenharmony_ci spk_shut_up &= 0xfe; 153362306a36Sopenharmony_ci spk_do_flush(); 153462306a36Sopenharmony_ci start_read_all_timer(vc, value + 1); 153562306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 153662306a36Sopenharmony_ci return NOTIFY_STOP; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 153962306a36Sopenharmony_ci return NOTIFY_OK; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci unsigned long flags; 154562306a36Sopenharmony_ci struct var_t *cursor_timeout; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 154862306a36Sopenharmony_ci spk_parked &= 0xfe; 154962306a36Sopenharmony_ci if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) { 155062306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 155162306a36Sopenharmony_ci return; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci spk_shut_up &= 0xfe; 155462306a36Sopenharmony_ci if (spk_no_intr) 155562306a36Sopenharmony_ci spk_do_flush(); 155662306a36Sopenharmony_ci/* the key press flushes if !no_inter but we want to flush on cursor 155762306a36Sopenharmony_ci * moves regardless of no_inter state 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci is_cursor = value + 1; 156062306a36Sopenharmony_ci old_cursor_pos = vc->vc_pos; 156162306a36Sopenharmony_ci old_cursor_x = vc->state.x; 156262306a36Sopenharmony_ci old_cursor_y = vc->state.y; 156362306a36Sopenharmony_ci speakup_console[vc->vc_num]->ht.cy = vc->state.y; 156462306a36Sopenharmony_ci cursor_con = vc->vc_num; 156562306a36Sopenharmony_ci if (cursor_track == CT_Highlight) 156662306a36Sopenharmony_ci reset_highlight_buffers(vc); 156762306a36Sopenharmony_ci cursor_timeout = spk_get_var(CURSOR_TIME); 156862306a36Sopenharmony_ci mod_timer(&cursor_timer, 156962306a36Sopenharmony_ci jiffies + msecs_to_jiffies(cursor_timeout->u.n.value)); 157062306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic void update_color_buffer(struct vc_data *vc, const u16 *ic, int len) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci int i, bi, hi; 157662306a36Sopenharmony_ci int vc_num = vc->vc_num; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci bi = (vc->vc_attr & 0x70) >> 4; 157962306a36Sopenharmony_ci hi = speakup_console[vc_num]->ht.highsize[bi]; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci i = 0; 158262306a36Sopenharmony_ci if (speakup_console[vc_num]->ht.highsize[bi] == 0) { 158362306a36Sopenharmony_ci speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos; 158462306a36Sopenharmony_ci speakup_console[vc_num]->ht.rx[bi] = vc->state.x; 158562306a36Sopenharmony_ci speakup_console[vc_num]->ht.ry[bi] = vc->state.y; 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci while ((hi < COLOR_BUFFER_SIZE) && (i < len)) { 158862306a36Sopenharmony_ci if (ic[i] > 32) { 158962306a36Sopenharmony_ci speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i]; 159062306a36Sopenharmony_ci hi++; 159162306a36Sopenharmony_ci } else if ((ic[i] == 32) && (hi != 0)) { 159262306a36Sopenharmony_ci if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] != 159362306a36Sopenharmony_ci 32) { 159462306a36Sopenharmony_ci speakup_console[vc_num]->ht.highbuf[bi][hi] = 159562306a36Sopenharmony_ci ic[i]; 159662306a36Sopenharmony_ci hi++; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci i++; 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci speakup_console[vc_num]->ht.highsize[bi] = hi; 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_cistatic void reset_highlight_buffers(struct vc_data *vc) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci int i; 160762306a36Sopenharmony_ci int vc_num = vc->vc_num; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci for (i = 0; i < 8; i++) 161062306a36Sopenharmony_ci speakup_console[vc_num]->ht.highsize[i] = 0; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic int count_highlight_color(struct vc_data *vc) 161462306a36Sopenharmony_ci{ 161562306a36Sopenharmony_ci int i, bg; 161662306a36Sopenharmony_ci int cc; 161762306a36Sopenharmony_ci int vc_num = vc->vc_num; 161862306a36Sopenharmony_ci u16 ch; 161962306a36Sopenharmony_ci u16 *start = (u16 *)vc->vc_origin; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci for (i = 0; i < 8; i++) 162262306a36Sopenharmony_ci speakup_console[vc_num]->ht.bgcount[i] = 0; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci for (i = 0; i < vc->vc_rows; i++) { 162562306a36Sopenharmony_ci u16 *end = start + vc->vc_cols * 2; 162662306a36Sopenharmony_ci u16 *ptr; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci for (ptr = start; ptr < end; ptr++) { 162962306a36Sopenharmony_ci ch = get_attributes(vc, ptr); 163062306a36Sopenharmony_ci bg = (ch & 0x70) >> 4; 163162306a36Sopenharmony_ci speakup_console[vc_num]->ht.bgcount[bg]++; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci start += vc->vc_size_row; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci cc = 0; 163762306a36Sopenharmony_ci for (i = 0; i < 8; i++) 163862306a36Sopenharmony_ci if (speakup_console[vc_num]->ht.bgcount[i] > 0) 163962306a36Sopenharmony_ci cc++; 164062306a36Sopenharmony_ci return cc; 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistatic int get_highlight_color(struct vc_data *vc) 164462306a36Sopenharmony_ci{ 164562306a36Sopenharmony_ci int i, j; 164662306a36Sopenharmony_ci unsigned int cptr[8]; 164762306a36Sopenharmony_ci int vc_num = vc->vc_num; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci for (i = 0; i < 8; i++) 165062306a36Sopenharmony_ci cptr[i] = i; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci for (i = 0; i < 7; i++) 165362306a36Sopenharmony_ci for (j = i + 1; j < 8; j++) 165462306a36Sopenharmony_ci if (speakup_console[vc_num]->ht.bgcount[cptr[i]] > 165562306a36Sopenharmony_ci speakup_console[vc_num]->ht.bgcount[cptr[j]]) 165662306a36Sopenharmony_ci swap(cptr[i], cptr[j]); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci for (i = 0; i < 8; i++) 165962306a36Sopenharmony_ci if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0) 166062306a36Sopenharmony_ci if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0) 166162306a36Sopenharmony_ci return cptr[i]; 166262306a36Sopenharmony_ci return -1; 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_cistatic int speak_highlight(struct vc_data *vc) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci int hc, d; 166862306a36Sopenharmony_ci int vc_num = vc->vc_num; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (count_highlight_color(vc) == 1) 167162306a36Sopenharmony_ci return 0; 167262306a36Sopenharmony_ci hc = get_highlight_color(vc); 167362306a36Sopenharmony_ci if (hc != -1) { 167462306a36Sopenharmony_ci d = vc->state.y - speakup_console[vc_num]->ht.cy; 167562306a36Sopenharmony_ci if ((d == 1) || (d == -1)) 167662306a36Sopenharmony_ci if (speakup_console[vc_num]->ht.ry[hc] != vc->state.y) 167762306a36Sopenharmony_ci return 0; 167862306a36Sopenharmony_ci spk_parked |= 0x01; 167962306a36Sopenharmony_ci spk_do_flush(); 168062306a36Sopenharmony_ci spkup_write(speakup_console[vc_num]->ht.highbuf[hc], 168162306a36Sopenharmony_ci speakup_console[vc_num]->ht.highsize[hc]); 168262306a36Sopenharmony_ci spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc]; 168362306a36Sopenharmony_ci spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc]; 168462306a36Sopenharmony_ci spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc]; 168562306a36Sopenharmony_ci return 1; 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci return 0; 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic void cursor_done(struct timer_list *unused) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci struct vc_data *vc = vc_cons[cursor_con].d; 169362306a36Sopenharmony_ci unsigned long flags; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci del_timer(&cursor_timer); 169662306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 169762306a36Sopenharmony_ci if (cursor_con != fg_console) { 169862306a36Sopenharmony_ci is_cursor = 0; 169962306a36Sopenharmony_ci goto out; 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci speakup_date(vc); 170262306a36Sopenharmony_ci if (win_enabled) { 170362306a36Sopenharmony_ci if (vc->state.x >= win_left && vc->state.x <= win_right && 170462306a36Sopenharmony_ci vc->state.y >= win_top && vc->state.y <= win_bottom) { 170562306a36Sopenharmony_ci spk_keydown = 0; 170662306a36Sopenharmony_ci is_cursor = 0; 170762306a36Sopenharmony_ci goto out; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci if (cursor_track == read_all_mode) { 171162306a36Sopenharmony_ci handle_cursor_read_all(vc, read_all_key); 171262306a36Sopenharmony_ci goto out; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci if (cursor_track == CT_Highlight) { 171562306a36Sopenharmony_ci if (speak_highlight(vc)) { 171662306a36Sopenharmony_ci spk_keydown = 0; 171762306a36Sopenharmony_ci is_cursor = 0; 171862306a36Sopenharmony_ci goto out; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci if (cursor_track == CT_Window) 172262306a36Sopenharmony_ci speakup_win_say(vc); 172362306a36Sopenharmony_ci else if (is_cursor == 1 || is_cursor == 4) 172462306a36Sopenharmony_ci say_line_from_to(vc, 0, vc->vc_cols, 0); 172562306a36Sopenharmony_ci else { 172662306a36Sopenharmony_ci if (spk_cur_phonetic == 1) 172762306a36Sopenharmony_ci say_phonetic_char(vc); 172862306a36Sopenharmony_ci else 172962306a36Sopenharmony_ci say_char(vc); 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci spk_keydown = 0; 173262306a36Sopenharmony_ci is_cursor = 0; 173362306a36Sopenharmony_ciout: 173462306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci/* called by: vt_notifier_call() */ 173862306a36Sopenharmony_cistatic void speakup_bs(struct vc_data *vc) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci unsigned long flags; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci if (!speakup_console[vc->vc_num]) 174362306a36Sopenharmony_ci return; 174462306a36Sopenharmony_ci if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) 174562306a36Sopenharmony_ci /* Speakup output, discard */ 174662306a36Sopenharmony_ci return; 174762306a36Sopenharmony_ci if (!spk_parked) 174862306a36Sopenharmony_ci speakup_date(vc); 174962306a36Sopenharmony_ci if (spk_shut_up || !synth) { 175062306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 175162306a36Sopenharmony_ci return; 175262306a36Sopenharmony_ci } 175362306a36Sopenharmony_ci if (vc->vc_num == fg_console && spk_keydown) { 175462306a36Sopenharmony_ci spk_keydown = 0; 175562306a36Sopenharmony_ci if (!is_cursor) 175662306a36Sopenharmony_ci say_char(vc); 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 175962306a36Sopenharmony_ci} 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci/* called by: vt_notifier_call() */ 176262306a36Sopenharmony_cistatic void speakup_con_write(struct vc_data *vc, u16 *str, int len) 176362306a36Sopenharmony_ci{ 176462306a36Sopenharmony_ci unsigned long flags; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci if ((vc->vc_num != fg_console) || spk_shut_up || !synth) 176762306a36Sopenharmony_ci return; 176862306a36Sopenharmony_ci if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) 176962306a36Sopenharmony_ci /* Speakup output, discard */ 177062306a36Sopenharmony_ci return; 177162306a36Sopenharmony_ci if (spk_bell_pos && spk_keydown && (vc->state.x == spk_bell_pos - 1)) 177262306a36Sopenharmony_ci bleep(3); 177362306a36Sopenharmony_ci if ((is_cursor) || (cursor_track == read_all_mode)) { 177462306a36Sopenharmony_ci if (cursor_track == CT_Highlight) 177562306a36Sopenharmony_ci update_color_buffer(vc, str, len); 177662306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 177762306a36Sopenharmony_ci return; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci if (win_enabled) { 178062306a36Sopenharmony_ci if (vc->state.x >= win_left && vc->state.x <= win_right && 178162306a36Sopenharmony_ci vc->state.y >= win_top && vc->state.y <= win_bottom) { 178262306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 178362306a36Sopenharmony_ci return; 178462306a36Sopenharmony_ci } 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci spkup_write(str, len); 178862306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 178962306a36Sopenharmony_ci} 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_cistatic void speakup_con_update(struct vc_data *vc) 179262306a36Sopenharmony_ci{ 179362306a36Sopenharmony_ci unsigned long flags; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (!speakup_console[vc->vc_num] || spk_parked || !synth) 179662306a36Sopenharmony_ci return; 179762306a36Sopenharmony_ci if (!spin_trylock_irqsave(&speakup_info.spinlock, flags)) 179862306a36Sopenharmony_ci /* Speakup output, discard */ 179962306a36Sopenharmony_ci return; 180062306a36Sopenharmony_ci speakup_date(vc); 180162306a36Sopenharmony_ci if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) { 180262306a36Sopenharmony_ci synth_printf("%s", spk_str_pause); 180362306a36Sopenharmony_ci spk_paused = true; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 180662306a36Sopenharmony_ci} 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_cistatic void do_handle_spec(struct vc_data *vc, u_char value, char up_flag) 180962306a36Sopenharmony_ci{ 181062306a36Sopenharmony_ci unsigned long flags; 181162306a36Sopenharmony_ci int on_off = 2; 181262306a36Sopenharmony_ci char *label; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci if (!synth || up_flag || spk_killed) 181562306a36Sopenharmony_ci return; 181662306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 181762306a36Sopenharmony_ci spk_shut_up &= 0xfe; 181862306a36Sopenharmony_ci if (spk_no_intr) 181962306a36Sopenharmony_ci spk_do_flush(); 182062306a36Sopenharmony_ci switch (value) { 182162306a36Sopenharmony_ci case KVAL(K_CAPS): 182262306a36Sopenharmony_ci label = spk_msg_get(MSG_KEYNAME_CAPSLOCK); 182362306a36Sopenharmony_ci on_off = vt_get_leds(fg_console, VC_CAPSLOCK); 182462306a36Sopenharmony_ci break; 182562306a36Sopenharmony_ci case KVAL(K_NUM): 182662306a36Sopenharmony_ci label = spk_msg_get(MSG_KEYNAME_NUMLOCK); 182762306a36Sopenharmony_ci on_off = vt_get_leds(fg_console, VC_NUMLOCK); 182862306a36Sopenharmony_ci break; 182962306a36Sopenharmony_ci case KVAL(K_HOLD): 183062306a36Sopenharmony_ci label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK); 183162306a36Sopenharmony_ci on_off = vt_get_leds(fg_console, VC_SCROLLOCK); 183262306a36Sopenharmony_ci if (speakup_console[vc->vc_num]) 183362306a36Sopenharmony_ci speakup_console[vc->vc_num]->tty_stopped = on_off; 183462306a36Sopenharmony_ci break; 183562306a36Sopenharmony_ci default: 183662306a36Sopenharmony_ci spk_parked &= 0xfe; 183762306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 183862306a36Sopenharmony_ci return; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci if (on_off < 2) 184162306a36Sopenharmony_ci synth_printf("%s %s\n", 184262306a36Sopenharmony_ci label, spk_msg_get(MSG_STATUS_START + on_off)); 184362306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 184462306a36Sopenharmony_ci} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_cistatic int inc_dec_var(u_char value) 184762306a36Sopenharmony_ci{ 184862306a36Sopenharmony_ci struct st_var_header *p_header; 184962306a36Sopenharmony_ci struct var_t *var_data; 185062306a36Sopenharmony_ci char num_buf[32]; 185162306a36Sopenharmony_ci char *cp = num_buf; 185262306a36Sopenharmony_ci char *pn; 185362306a36Sopenharmony_ci int var_id = (int)value - VAR_START; 185462306a36Sopenharmony_ci int how = (var_id & 1) ? E_INC : E_DEC; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci var_id = var_id / 2 + FIRST_SET_VAR; 185762306a36Sopenharmony_ci p_header = spk_get_var_header(var_id); 185862306a36Sopenharmony_ci if (!p_header) 185962306a36Sopenharmony_ci return -1; 186062306a36Sopenharmony_ci if (p_header->var_type != VAR_NUM) 186162306a36Sopenharmony_ci return -1; 186262306a36Sopenharmony_ci var_data = p_header->data; 186362306a36Sopenharmony_ci if (spk_set_num_var(1, p_header, how) != 0) 186462306a36Sopenharmony_ci return -1; 186562306a36Sopenharmony_ci if (!spk_close_press) { 186662306a36Sopenharmony_ci for (pn = p_header->name; *pn; pn++) { 186762306a36Sopenharmony_ci if (*pn == '_') 186862306a36Sopenharmony_ci *cp = SPACE; 186962306a36Sopenharmony_ci else 187062306a36Sopenharmony_ci *cp++ = *pn; 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ", 187462306a36Sopenharmony_ci var_data->u.n.value); 187562306a36Sopenharmony_ci synth_printf("%s", num_buf); 187662306a36Sopenharmony_ci return 0; 187762306a36Sopenharmony_ci} 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_cistatic void speakup_win_set(struct vc_data *vc) 188062306a36Sopenharmony_ci{ 188162306a36Sopenharmony_ci char info[40]; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci if (win_start > 1) { 188462306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET)); 188562306a36Sopenharmony_ci return; 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci if (spk_x < win_left || spk_y < win_top) { 188862306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START)); 188962306a36Sopenharmony_ci return; 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci if (win_start && spk_x == win_left && spk_y == win_top) { 189262306a36Sopenharmony_ci win_left = 0; 189362306a36Sopenharmony_ci win_right = vc->vc_cols - 1; 189462306a36Sopenharmony_ci win_bottom = spk_y; 189562306a36Sopenharmony_ci snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE), 189662306a36Sopenharmony_ci (int)win_top + 1); 189762306a36Sopenharmony_ci } else { 189862306a36Sopenharmony_ci if (!win_start) { 189962306a36Sopenharmony_ci win_top = spk_y; 190062306a36Sopenharmony_ci win_left = spk_x; 190162306a36Sopenharmony_ci } else { 190262306a36Sopenharmony_ci win_bottom = spk_y; 190362306a36Sopenharmony_ci win_right = spk_x; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY), 190662306a36Sopenharmony_ci (win_start) ? 190762306a36Sopenharmony_ci spk_msg_get(MSG_END) : spk_msg_get(MSG_START), 190862306a36Sopenharmony_ci (int)spk_y + 1, (int)spk_x + 1); 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci synth_printf("%s\n", info); 191162306a36Sopenharmony_ci win_start++; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic void speakup_win_clear(struct vc_data *vc) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci win_top = 0; 191762306a36Sopenharmony_ci win_bottom = 0; 191862306a36Sopenharmony_ci win_left = 0; 191962306a36Sopenharmony_ci win_right = 0; 192062306a36Sopenharmony_ci win_start = 0; 192162306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED)); 192262306a36Sopenharmony_ci} 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_cistatic void speakup_win_enable(struct vc_data *vc) 192562306a36Sopenharmony_ci{ 192662306a36Sopenharmony_ci if (win_start < 2) { 192762306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW)); 192862306a36Sopenharmony_ci return; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci win_enabled ^= 1; 193162306a36Sopenharmony_ci if (win_enabled) 193262306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED)); 193362306a36Sopenharmony_ci else 193462306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED)); 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic void speakup_bits(struct vc_data *vc) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci int val = this_speakup_key - (FIRST_EDIT_BITS - 1); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci if (spk_special_handler || val < 1 || val > 6) { 194262306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_ERROR)); 194362306a36Sopenharmony_ci return; 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci pb_edit = &spk_punc_info[val]; 194662306a36Sopenharmony_ci synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name); 194762306a36Sopenharmony_ci spk_special_handler = edit_bits; 194862306a36Sopenharmony_ci} 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_cistatic int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci static u_char goto_buf[8]; 195362306a36Sopenharmony_ci static int num; 195462306a36Sopenharmony_ci int maxlen; 195562306a36Sopenharmony_ci char *cp; 195662306a36Sopenharmony_ci u16 wch; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci if (type == KT_SPKUP && ch == SPEAKUP_GOTO) 195962306a36Sopenharmony_ci goto do_goto; 196062306a36Sopenharmony_ci if (type == KT_LATIN && ch == '\n') 196162306a36Sopenharmony_ci goto do_goto; 196262306a36Sopenharmony_ci if (type != 0) 196362306a36Sopenharmony_ci goto oops; 196462306a36Sopenharmony_ci if (ch == 8) { 196562306a36Sopenharmony_ci u16 wch; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (num == 0) 196862306a36Sopenharmony_ci return -1; 196962306a36Sopenharmony_ci wch = goto_buf[--num]; 197062306a36Sopenharmony_ci goto_buf[num] = '\0'; 197162306a36Sopenharmony_ci spkup_write(&wch, 1); 197262306a36Sopenharmony_ci return 1; 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci if (ch < '+' || ch > 'y') 197562306a36Sopenharmony_ci goto oops; 197662306a36Sopenharmony_ci wch = ch; 197762306a36Sopenharmony_ci goto_buf[num++] = ch; 197862306a36Sopenharmony_ci goto_buf[num] = '\0'; 197962306a36Sopenharmony_ci spkup_write(&wch, 1); 198062306a36Sopenharmony_ci maxlen = (*goto_buf >= '0') ? 3 : 4; 198162306a36Sopenharmony_ci if ((ch == '+' || ch == '-') && num == 1) 198262306a36Sopenharmony_ci return 1; 198362306a36Sopenharmony_ci if (ch >= '0' && ch <= '9' && num < maxlen) 198462306a36Sopenharmony_ci return 1; 198562306a36Sopenharmony_ci if (num < maxlen - 1 || num > maxlen) 198662306a36Sopenharmony_ci goto oops; 198762306a36Sopenharmony_ci if (ch < 'x' || ch > 'y') { 198862306a36Sopenharmony_cioops: 198962306a36Sopenharmony_ci if (!spk_killed) 199062306a36Sopenharmony_ci synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED)); 199162306a36Sopenharmony_ci goto_buf[num = 0] = '\0'; 199262306a36Sopenharmony_ci spk_special_handler = NULL; 199362306a36Sopenharmony_ci return 1; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci /* Do not replace with kstrtoul: here we need cp to be updated */ 199762306a36Sopenharmony_ci goto_pos = simple_strtoul(goto_buf, &cp, 10); 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci if (*cp == 'x') { 200062306a36Sopenharmony_ci if (*goto_buf < '0') 200162306a36Sopenharmony_ci goto_pos += spk_x; 200262306a36Sopenharmony_ci else if (goto_pos > 0) 200362306a36Sopenharmony_ci goto_pos--; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci if (goto_pos >= vc->vc_cols) 200662306a36Sopenharmony_ci goto_pos = vc->vc_cols - 1; 200762306a36Sopenharmony_ci goto_x = 1; 200862306a36Sopenharmony_ci } else { 200962306a36Sopenharmony_ci if (*goto_buf < '0') 201062306a36Sopenharmony_ci goto_pos += spk_y; 201162306a36Sopenharmony_ci else if (goto_pos > 0) 201262306a36Sopenharmony_ci goto_pos--; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci if (goto_pos >= vc->vc_rows) 201562306a36Sopenharmony_ci goto_pos = vc->vc_rows - 1; 201662306a36Sopenharmony_ci goto_x = 0; 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci goto_buf[num = 0] = '\0'; 201962306a36Sopenharmony_cido_goto: 202062306a36Sopenharmony_ci spk_special_handler = NULL; 202162306a36Sopenharmony_ci spk_parked |= 0x01; 202262306a36Sopenharmony_ci if (goto_x) { 202362306a36Sopenharmony_ci spk_pos -= spk_x * 2; 202462306a36Sopenharmony_ci spk_x = goto_pos; 202562306a36Sopenharmony_ci spk_pos += goto_pos * 2; 202662306a36Sopenharmony_ci say_word(vc); 202762306a36Sopenharmony_ci } else { 202862306a36Sopenharmony_ci spk_y = goto_pos; 202962306a36Sopenharmony_ci spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row); 203062306a36Sopenharmony_ci say_line(vc); 203162306a36Sopenharmony_ci } 203262306a36Sopenharmony_ci return 1; 203362306a36Sopenharmony_ci} 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_cistatic void speakup_goto(struct vc_data *vc) 203662306a36Sopenharmony_ci{ 203762306a36Sopenharmony_ci if (spk_special_handler) { 203862306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_ERROR)); 203962306a36Sopenharmony_ci return; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci synth_printf("%s\n", spk_msg_get(MSG_GOTO)); 204262306a36Sopenharmony_ci spk_special_handler = handle_goto; 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_cistatic void speakup_help(struct vc_data *vc) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0); 204862306a36Sopenharmony_ci} 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_cistatic void do_nothing(struct vc_data *vc) 205162306a36Sopenharmony_ci{ 205262306a36Sopenharmony_ci return; /* flush done in do_spkup */ 205362306a36Sopenharmony_ci} 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_cistatic u_char key_speakup, spk_key_locked; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_cistatic void speakup_lock(struct vc_data *vc) 205862306a36Sopenharmony_ci{ 205962306a36Sopenharmony_ci if (!spk_key_locked) { 206062306a36Sopenharmony_ci spk_key_locked = 16; 206162306a36Sopenharmony_ci key_speakup = 16; 206262306a36Sopenharmony_ci } else { 206362306a36Sopenharmony_ci spk_key_locked = 0; 206462306a36Sopenharmony_ci key_speakup = 0; 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci} 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_citypedef void (*spkup_hand) (struct vc_data *); 206962306a36Sopenharmony_cistatic spkup_hand spkup_handler[] = { 207062306a36Sopenharmony_ci /* must be ordered same as defines in speakup.h */ 207162306a36Sopenharmony_ci do_nothing, speakup_goto, speech_kill, speakup_shut_up, 207262306a36Sopenharmony_ci speakup_cut, speakup_paste, say_first_char, say_last_char, 207362306a36Sopenharmony_ci say_char, say_prev_char, say_next_char, 207462306a36Sopenharmony_ci say_word, say_prev_word, say_next_word, 207562306a36Sopenharmony_ci say_line, say_prev_line, say_next_line, 207662306a36Sopenharmony_ci top_edge, bottom_edge, left_edge, right_edge, 207762306a36Sopenharmony_ci spell_word, spell_word, say_screen, 207862306a36Sopenharmony_ci say_position, say_attributes, 207962306a36Sopenharmony_ci speakup_off, speakup_parked, say_line, /* this is for indent */ 208062306a36Sopenharmony_ci say_from_top, say_to_bottom, 208162306a36Sopenharmony_ci say_from_left, say_to_right, 208262306a36Sopenharmony_ci say_char_num, speakup_bits, speakup_bits, say_phonetic_char, 208362306a36Sopenharmony_ci speakup_bits, speakup_bits, speakup_bits, 208462306a36Sopenharmony_ci speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say, 208562306a36Sopenharmony_ci speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL 208662306a36Sopenharmony_ci}; 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_cistatic void do_spkup(struct vc_data *vc, u_char value) 208962306a36Sopenharmony_ci{ 209062306a36Sopenharmony_ci if (spk_killed && value != SPEECH_KILL) 209162306a36Sopenharmony_ci return; 209262306a36Sopenharmony_ci spk_keydown = 0; 209362306a36Sopenharmony_ci spk_lastkey = 0; 209462306a36Sopenharmony_ci spk_shut_up &= 0xfe; 209562306a36Sopenharmony_ci this_speakup_key = value; 209662306a36Sopenharmony_ci if (value < SPKUP_MAX_FUNC && spkup_handler[value]) { 209762306a36Sopenharmony_ci spk_do_flush(); 209862306a36Sopenharmony_ci (*spkup_handler[value]) (vc); 209962306a36Sopenharmony_ci } else { 210062306a36Sopenharmony_ci if (inc_dec_var(value) < 0) 210162306a36Sopenharmony_ci bleep(9); 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic const char *pad_chars = "0123456789+-*/\015,.?()"; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_cistatic int 210862306a36Sopenharmony_cispeakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym, 210962306a36Sopenharmony_ci int up_flag) 211062306a36Sopenharmony_ci{ 211162306a36Sopenharmony_ci unsigned long flags; 211262306a36Sopenharmony_ci int kh; 211362306a36Sopenharmony_ci u_char *key_info; 211462306a36Sopenharmony_ci u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0; 211562306a36Sopenharmony_ci u_char shift_info, offset; 211662306a36Sopenharmony_ci int ret = 0; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci if (!synth) 211962306a36Sopenharmony_ci return 0; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci spin_lock_irqsave(&speakup_info.spinlock, flags); 212262306a36Sopenharmony_ci tty = vc->port.tty; 212362306a36Sopenharmony_ci if (type >= 0xf0) 212462306a36Sopenharmony_ci type -= 0xf0; 212562306a36Sopenharmony_ci if (type == KT_PAD && 212662306a36Sopenharmony_ci (vt_get_leds(fg_console, VC_NUMLOCK))) { 212762306a36Sopenharmony_ci if (up_flag) { 212862306a36Sopenharmony_ci spk_keydown = 0; 212962306a36Sopenharmony_ci goto out; 213062306a36Sopenharmony_ci } 213162306a36Sopenharmony_ci value = pad_chars[value]; 213262306a36Sopenharmony_ci spk_lastkey = value; 213362306a36Sopenharmony_ci spk_keydown++; 213462306a36Sopenharmony_ci spk_parked &= 0xfe; 213562306a36Sopenharmony_ci goto no_map; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci if (keycode >= MAX_KEY) 213862306a36Sopenharmony_ci goto no_map; 213962306a36Sopenharmony_ci key_info = spk_our_keys[keycode]; 214062306a36Sopenharmony_ci if (!key_info) 214162306a36Sopenharmony_ci goto no_map; 214262306a36Sopenharmony_ci /* Check valid read all mode keys */ 214362306a36Sopenharmony_ci if ((cursor_track == read_all_mode) && (!up_flag)) { 214462306a36Sopenharmony_ci switch (value) { 214562306a36Sopenharmony_ci case KVAL(K_DOWN): 214662306a36Sopenharmony_ci case KVAL(K_UP): 214762306a36Sopenharmony_ci case KVAL(K_LEFT): 214862306a36Sopenharmony_ci case KVAL(K_RIGHT): 214962306a36Sopenharmony_ci case KVAL(K_PGUP): 215062306a36Sopenharmony_ci case KVAL(K_PGDN): 215162306a36Sopenharmony_ci break; 215262306a36Sopenharmony_ci default: 215362306a36Sopenharmony_ci stop_read_all(vc); 215462306a36Sopenharmony_ci break; 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci shift_info = (shift_state & 0x0f) + key_speakup; 215862306a36Sopenharmony_ci offset = spk_shift_table[shift_info]; 215962306a36Sopenharmony_ci if (offset) { 216062306a36Sopenharmony_ci new_key = key_info[offset]; 216162306a36Sopenharmony_ci if (new_key) { 216262306a36Sopenharmony_ci ret = 1; 216362306a36Sopenharmony_ci if (new_key == SPK_KEY) { 216462306a36Sopenharmony_ci if (!spk_key_locked) 216562306a36Sopenharmony_ci key_speakup = (up_flag) ? 0 : 16; 216662306a36Sopenharmony_ci if (up_flag || spk_killed) 216762306a36Sopenharmony_ci goto out; 216862306a36Sopenharmony_ci spk_shut_up &= 0xfe; 216962306a36Sopenharmony_ci spk_do_flush(); 217062306a36Sopenharmony_ci goto out; 217162306a36Sopenharmony_ci } 217262306a36Sopenharmony_ci if (up_flag) 217362306a36Sopenharmony_ci goto out; 217462306a36Sopenharmony_ci if (last_keycode == keycode && 217562306a36Sopenharmony_ci time_after(last_spk_jiffy + MAX_DELAY, jiffies)) { 217662306a36Sopenharmony_ci spk_close_press = 1; 217762306a36Sopenharmony_ci offset = spk_shift_table[shift_info + 32]; 217862306a36Sopenharmony_ci /* double press? */ 217962306a36Sopenharmony_ci if (offset && key_info[offset]) 218062306a36Sopenharmony_ci new_key = key_info[offset]; 218162306a36Sopenharmony_ci } 218262306a36Sopenharmony_ci last_keycode = keycode; 218362306a36Sopenharmony_ci last_spk_jiffy = jiffies; 218462306a36Sopenharmony_ci type = KT_SPKUP; 218562306a36Sopenharmony_ci value = new_key; 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_cino_map: 218962306a36Sopenharmony_ci if (type == KT_SPKUP && !spk_special_handler) { 219062306a36Sopenharmony_ci do_spkup(vc, new_key); 219162306a36Sopenharmony_ci spk_close_press = 0; 219262306a36Sopenharmony_ci ret = 1; 219362306a36Sopenharmony_ci goto out; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci if (up_flag || spk_killed || type == KT_SHIFT) 219662306a36Sopenharmony_ci goto out; 219762306a36Sopenharmony_ci spk_shut_up &= 0xfe; 219862306a36Sopenharmony_ci kh = (value == KVAL(K_DOWN)) || 219962306a36Sopenharmony_ci (value == KVAL(K_UP)) || 220062306a36Sopenharmony_ci (value == KVAL(K_LEFT)) || 220162306a36Sopenharmony_ci (value == KVAL(K_RIGHT)); 220262306a36Sopenharmony_ci if ((cursor_track != read_all_mode) || !kh) 220362306a36Sopenharmony_ci if (!spk_no_intr) 220462306a36Sopenharmony_ci spk_do_flush(); 220562306a36Sopenharmony_ci if (spk_special_handler) { 220662306a36Sopenharmony_ci if (type == KT_SPEC && value == 1) { 220762306a36Sopenharmony_ci value = '\n'; 220862306a36Sopenharmony_ci type = KT_LATIN; 220962306a36Sopenharmony_ci } else if (type == KT_LETTER) { 221062306a36Sopenharmony_ci type = KT_LATIN; 221162306a36Sopenharmony_ci } else if (value == 0x7f) { 221262306a36Sopenharmony_ci value = 8; /* make del = backspace */ 221362306a36Sopenharmony_ci } 221462306a36Sopenharmony_ci ret = (*spk_special_handler) (vc, type, value, keycode); 221562306a36Sopenharmony_ci spk_close_press = 0; 221662306a36Sopenharmony_ci if (ret < 0) 221762306a36Sopenharmony_ci bleep(9); 221862306a36Sopenharmony_ci goto out; 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci last_keycode = 0; 222162306a36Sopenharmony_ciout: 222262306a36Sopenharmony_ci spin_unlock_irqrestore(&speakup_info.spinlock, flags); 222362306a36Sopenharmony_ci return ret; 222462306a36Sopenharmony_ci} 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_cistatic int keyboard_notifier_call(struct notifier_block *nb, 222762306a36Sopenharmony_ci unsigned long code, void *_param) 222862306a36Sopenharmony_ci{ 222962306a36Sopenharmony_ci struct keyboard_notifier_param *param = _param; 223062306a36Sopenharmony_ci struct vc_data *vc = param->vc; 223162306a36Sopenharmony_ci int up = !param->down; 223262306a36Sopenharmony_ci int ret = NOTIFY_OK; 223362306a36Sopenharmony_ci static int keycode; /* to hold the current keycode */ 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci in_keyboard_notifier = 1; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci if (vc->vc_mode == KD_GRAPHICS) 223862306a36Sopenharmony_ci goto out; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci /* 224162306a36Sopenharmony_ci * First, determine whether we are handling a fake keypress on 224262306a36Sopenharmony_ci * the current processor. If we are, then return NOTIFY_OK, 224362306a36Sopenharmony_ci * to pass the keystroke up the chain. This prevents us from 224462306a36Sopenharmony_ci * trying to take the Speakup lock while it is held by the 224562306a36Sopenharmony_ci * processor on which the simulated keystroke was generated. 224662306a36Sopenharmony_ci * Also, the simulated keystrokes should be ignored by Speakup. 224762306a36Sopenharmony_ci */ 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci if (speakup_fake_key_pressed()) 225062306a36Sopenharmony_ci goto out; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci switch (code) { 225362306a36Sopenharmony_ci case KBD_KEYCODE: 225462306a36Sopenharmony_ci /* speakup requires keycode and keysym currently */ 225562306a36Sopenharmony_ci keycode = param->value; 225662306a36Sopenharmony_ci break; 225762306a36Sopenharmony_ci case KBD_UNBOUND_KEYCODE: 225862306a36Sopenharmony_ci /* not used yet */ 225962306a36Sopenharmony_ci break; 226062306a36Sopenharmony_ci case KBD_UNICODE: 226162306a36Sopenharmony_ci /* not used yet */ 226262306a36Sopenharmony_ci break; 226362306a36Sopenharmony_ci case KBD_KEYSYM: 226462306a36Sopenharmony_ci if (speakup_key(vc, param->shift, keycode, param->value, up)) 226562306a36Sopenharmony_ci ret = NOTIFY_STOP; 226662306a36Sopenharmony_ci else if (KTYP(param->value) == KT_CUR) 226762306a36Sopenharmony_ci ret = pre_handle_cursor(vc, KVAL(param->value), up); 226862306a36Sopenharmony_ci break; 226962306a36Sopenharmony_ci case KBD_POST_KEYSYM:{ 227062306a36Sopenharmony_ci unsigned char type = KTYP(param->value) - 0xf0; 227162306a36Sopenharmony_ci unsigned char val = KVAL(param->value); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci switch (type) { 227462306a36Sopenharmony_ci case KT_SHIFT: 227562306a36Sopenharmony_ci do_handle_shift(vc, val, up); 227662306a36Sopenharmony_ci break; 227762306a36Sopenharmony_ci case KT_LATIN: 227862306a36Sopenharmony_ci case KT_LETTER: 227962306a36Sopenharmony_ci do_handle_latin(vc, val, up); 228062306a36Sopenharmony_ci break; 228162306a36Sopenharmony_ci case KT_CUR: 228262306a36Sopenharmony_ci do_handle_cursor(vc, val, up); 228362306a36Sopenharmony_ci break; 228462306a36Sopenharmony_ci case KT_SPEC: 228562306a36Sopenharmony_ci do_handle_spec(vc, val, up); 228662306a36Sopenharmony_ci break; 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci break; 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_ci } 229162306a36Sopenharmony_ciout: 229262306a36Sopenharmony_ci in_keyboard_notifier = 0; 229362306a36Sopenharmony_ci return ret; 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_cistatic int vt_notifier_call(struct notifier_block *nb, 229762306a36Sopenharmony_ci unsigned long code, void *_param) 229862306a36Sopenharmony_ci{ 229962306a36Sopenharmony_ci struct vt_notifier_param *param = _param; 230062306a36Sopenharmony_ci struct vc_data *vc = param->vc; 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci switch (code) { 230362306a36Sopenharmony_ci case VT_ALLOCATE: 230462306a36Sopenharmony_ci if (vc->vc_mode == KD_TEXT) 230562306a36Sopenharmony_ci speakup_allocate(vc, GFP_ATOMIC); 230662306a36Sopenharmony_ci break; 230762306a36Sopenharmony_ci case VT_DEALLOCATE: 230862306a36Sopenharmony_ci speakup_deallocate(vc); 230962306a36Sopenharmony_ci break; 231062306a36Sopenharmony_ci case VT_WRITE: 231162306a36Sopenharmony_ci if (param->c == '\b') { 231262306a36Sopenharmony_ci speakup_bs(vc); 231362306a36Sopenharmony_ci } else { 231462306a36Sopenharmony_ci u16 d = param->c; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci speakup_con_write(vc, &d, 1); 231762306a36Sopenharmony_ci } 231862306a36Sopenharmony_ci break; 231962306a36Sopenharmony_ci case VT_UPDATE: 232062306a36Sopenharmony_ci speakup_con_update(vc); 232162306a36Sopenharmony_ci break; 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci return NOTIFY_OK; 232462306a36Sopenharmony_ci} 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci/* called by: module_exit() */ 232762306a36Sopenharmony_cistatic void __exit speakup_exit(void) 232862306a36Sopenharmony_ci{ 232962306a36Sopenharmony_ci int i; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci unregister_keyboard_notifier(&keyboard_notifier_block); 233262306a36Sopenharmony_ci unregister_vt_notifier(&vt_notifier_block); 233362306a36Sopenharmony_ci speakup_unregister_devsynth(); 233462306a36Sopenharmony_ci speakup_cancel_selection(); 233562306a36Sopenharmony_ci speakup_cancel_paste(); 233662306a36Sopenharmony_ci del_timer_sync(&cursor_timer); 233762306a36Sopenharmony_ci kthread_stop(speakup_task); 233862306a36Sopenharmony_ci speakup_task = NULL; 233962306a36Sopenharmony_ci mutex_lock(&spk_mutex); 234062306a36Sopenharmony_ci synth_release(); 234162306a36Sopenharmony_ci mutex_unlock(&spk_mutex); 234262306a36Sopenharmony_ci spk_ttyio_unregister_ldisc(); 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci speakup_kobj_exit(); 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci for (i = 0; i < MAX_NR_CONSOLES; i++) 234762306a36Sopenharmony_ci kfree(speakup_console[i]); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci speakup_remove_virtual_keyboard(); 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci for (i = 0; i < MAXVARS; i++) 235262306a36Sopenharmony_ci speakup_unregister_var(i); 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 235562306a36Sopenharmony_ci if (spk_characters[i] != spk_default_chars[i]) 235662306a36Sopenharmony_ci kfree(spk_characters[i]); 235762306a36Sopenharmony_ci } 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci spk_free_user_msgs(); 236062306a36Sopenharmony_ci} 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci/* call by: module_init() */ 236362306a36Sopenharmony_cistatic int __init speakup_init(void) 236462306a36Sopenharmony_ci{ 236562306a36Sopenharmony_ci int i; 236662306a36Sopenharmony_ci long err = 0; 236762306a36Sopenharmony_ci struct vc_data *vc = vc_cons[fg_console].d; 236862306a36Sopenharmony_ci struct var_t *var; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci /* These first few initializations cannot fail. */ 237162306a36Sopenharmony_ci spk_initialize_msgs(); /* Initialize arrays for i18n. */ 237262306a36Sopenharmony_ci spk_reset_default_chars(); 237362306a36Sopenharmony_ci spk_reset_default_chartab(); 237462306a36Sopenharmony_ci spk_strlwr(synth_name); 237562306a36Sopenharmony_ci spk_vars[0].u.n.high = vc->vc_cols; 237662306a36Sopenharmony_ci for (var = spk_vars; var->var_id != MAXVARS; var++) 237762306a36Sopenharmony_ci speakup_register_var(var); 237862306a36Sopenharmony_ci for (var = synth_time_vars; 237962306a36Sopenharmony_ci (var->var_id >= 0) && (var->var_id < MAXVARS); var++) 238062306a36Sopenharmony_ci speakup_register_var(var); 238162306a36Sopenharmony_ci for (i = 1; spk_punc_info[i].mask != 0; i++) 238262306a36Sopenharmony_ci spk_set_mask_bits(NULL, i, 2); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci spk_set_key_info(spk_key_defaults, spk_key_buf); 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci /* From here on out, initializations can fail. */ 238762306a36Sopenharmony_ci err = speakup_add_virtual_keyboard(); 238862306a36Sopenharmony_ci if (err) 238962306a36Sopenharmony_ci goto error_virtkeyboard; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci for (i = 0; i < MAX_NR_CONSOLES; i++) 239262306a36Sopenharmony_ci if (vc_cons[i].d) { 239362306a36Sopenharmony_ci err = speakup_allocate(vc_cons[i].d, GFP_KERNEL); 239462306a36Sopenharmony_ci if (err) 239562306a36Sopenharmony_ci goto error_kobjects; 239662306a36Sopenharmony_ci } 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci if (spk_quiet_boot) 239962306a36Sopenharmony_ci spk_shut_up |= 0x01; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci err = speakup_kobj_init(); 240262306a36Sopenharmony_ci if (err) 240362306a36Sopenharmony_ci goto error_kobjects; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci spk_ttyio_register_ldisc(); 240662306a36Sopenharmony_ci synth_init(synth_name); 240762306a36Sopenharmony_ci speakup_register_devsynth(); 240862306a36Sopenharmony_ci /* 240962306a36Sopenharmony_ci * register_devsynth might fail, but this error is not fatal. 241062306a36Sopenharmony_ci * /dev/synth is an extra feature; the rest of Speakup 241162306a36Sopenharmony_ci * will work fine without it. 241262306a36Sopenharmony_ci */ 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci err = register_keyboard_notifier(&keyboard_notifier_block); 241562306a36Sopenharmony_ci if (err) 241662306a36Sopenharmony_ci goto error_kbdnotifier; 241762306a36Sopenharmony_ci err = register_vt_notifier(&vt_notifier_block); 241862306a36Sopenharmony_ci if (err) 241962306a36Sopenharmony_ci goto error_vtnotifier; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci speakup_task = kthread_create(speakup_thread, NULL, "speakup"); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci if (IS_ERR(speakup_task)) { 242462306a36Sopenharmony_ci err = PTR_ERR(speakup_task); 242562306a36Sopenharmony_ci goto error_task; 242662306a36Sopenharmony_ci } 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci set_user_nice(speakup_task, 10); 242962306a36Sopenharmony_ci wake_up_process(speakup_task); 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci pr_info("speakup %s: initialized\n", SPEAKUP_VERSION); 243262306a36Sopenharmony_ci pr_info("synth name on entry is: %s\n", synth_name); 243362306a36Sopenharmony_ci goto out; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_cierror_task: 243662306a36Sopenharmony_ci unregister_vt_notifier(&vt_notifier_block); 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_cierror_vtnotifier: 243962306a36Sopenharmony_ci unregister_keyboard_notifier(&keyboard_notifier_block); 244062306a36Sopenharmony_ci del_timer(&cursor_timer); 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_cierror_kbdnotifier: 244362306a36Sopenharmony_ci speakup_unregister_devsynth(); 244462306a36Sopenharmony_ci mutex_lock(&spk_mutex); 244562306a36Sopenharmony_ci synth_release(); 244662306a36Sopenharmony_ci mutex_unlock(&spk_mutex); 244762306a36Sopenharmony_ci speakup_kobj_exit(); 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_cierror_kobjects: 245062306a36Sopenharmony_ci for (i = 0; i < MAX_NR_CONSOLES; i++) 245162306a36Sopenharmony_ci kfree(speakup_console[i]); 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci speakup_remove_virtual_keyboard(); 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_cierror_virtkeyboard: 245662306a36Sopenharmony_ci for (i = 0; i < MAXVARS; i++) 245762306a36Sopenharmony_ci speakup_unregister_var(i); 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 246062306a36Sopenharmony_ci if (spk_characters[i] != spk_default_chars[i]) 246162306a36Sopenharmony_ci kfree(spk_characters[i]); 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci spk_free_user_msgs(); 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ciout: 246762306a36Sopenharmony_ci return err; 246862306a36Sopenharmony_ci} 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_cimodule_param_named(bell_pos, spk_vars[BELL_POS_ID].u.n.default_val, int, 0444); 247162306a36Sopenharmony_cimodule_param_named(spell_delay, spk_vars[SPELL_DELAY_ID].u.n.default_val, int, 0444); 247262306a36Sopenharmony_cimodule_param_named(attrib_bleep, spk_vars[ATTRIB_BLEEP_ID].u.n.default_val, int, 0444); 247362306a36Sopenharmony_cimodule_param_named(bleeps, spk_vars[BLEEPS_ID].u.n.default_val, int, 0444); 247462306a36Sopenharmony_cimodule_param_named(bleep_time, spk_vars[BLEEP_TIME_ID].u.n.default_val, int, 0444); 247562306a36Sopenharmony_cimodule_param_named(punc_level, spk_vars[PUNC_LEVEL_ID].u.n.default_val, int, 0444); 247662306a36Sopenharmony_cimodule_param_named(reading_punc, spk_vars[READING_PUNC_ID].u.n.default_val, int, 0444); 247762306a36Sopenharmony_cimodule_param_named(cursor_time, spk_vars[CURSOR_TIME_ID].u.n.default_val, int, 0444); 247862306a36Sopenharmony_cimodule_param_named(say_control, spk_vars[SAY_CONTROL_ID].u.n.default_val, int, 0444); 247962306a36Sopenharmony_cimodule_param_named(say_word_ctl, spk_vars[SAY_WORD_CTL_ID].u.n.default_val, int, 0444); 248062306a36Sopenharmony_cimodule_param_named(no_interrupt, spk_vars[NO_INTERRUPT_ID].u.n.default_val, int, 0444); 248162306a36Sopenharmony_cimodule_param_named(key_echo, spk_vars[KEY_ECHO_ID].u.n.default_val, int, 0444); 248262306a36Sopenharmony_cimodule_param_named(cur_phonetic, spk_vars[CUR_PHONETIC_ID].u.n.default_val, int, 0444); 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ciMODULE_PARM_DESC(bell_pos, "This works much like a typewriter bell. If for example 72 is echoed to bell_pos, it will beep the PC speaker when typing on a line past character 72."); 248562306a36Sopenharmony_ciMODULE_PARM_DESC(spell_delay, "This controls how fast a word is spelled when speakup's spell word review command is pressed."); 248662306a36Sopenharmony_ciMODULE_PARM_DESC(attrib_bleep, "Beeps the PC speaker when there is an attribute change such as background color when using speakup review commands. One = on, zero = off."); 248762306a36Sopenharmony_ciMODULE_PARM_DESC(bleeps, "This controls whether one hears beeps through the PC speaker when using speakup review commands."); 248862306a36Sopenharmony_ciMODULE_PARM_DESC(bleep_time, "This controls the duration of the PC speaker beeps speakup produces."); 248962306a36Sopenharmony_ciMODULE_PARM_DESC(punc_level, "Controls the level of punctuation spoken as the screen is displayed, not reviewed."); 249062306a36Sopenharmony_ciMODULE_PARM_DESC(reading_punc, "It controls the level of punctuation when reviewing the screen with speakup's screen review commands."); 249162306a36Sopenharmony_ciMODULE_PARM_DESC(cursor_time, "This controls cursor delay when using arrow keys."); 249262306a36Sopenharmony_ciMODULE_PARM_DESC(say_control, "This controls if speakup speaks shift, alt and control when those keys are pressed or not."); 249362306a36Sopenharmony_ciMODULE_PARM_DESC(say_word_ctl, "Sets the say_word_ctl on load."); 249462306a36Sopenharmony_ciMODULE_PARM_DESC(no_interrupt, "Controls if typing interrupts output from speakup."); 249562306a36Sopenharmony_ciMODULE_PARM_DESC(key_echo, "Controls if speakup speaks keys when they are typed. One = on zero = off or don't echo keys."); 249662306a36Sopenharmony_ciMODULE_PARM_DESC(cur_phonetic, "Controls if speakup speaks letters phonetically during navigation. One = on zero = off or don't speak phonetically."); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_cimodule_init(speakup_init); 249962306a36Sopenharmony_cimodule_exit(speakup_exit); 2500