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