18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/* speakup.c
38c2ecf20Sopenharmony_ci * review functions for the speakup screen review package.
48c2ecf20Sopenharmony_ci * originally written by: Kirk Reiser and Andy Berdan.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * extensively modified by David Borowski.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci ** Copyright (C) 1998  Kirk Reiser.
98c2ecf20Sopenharmony_ci *  Copyright (C) 2003  David Borowski.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/vt.h>
148c2ecf20Sopenharmony_ci#include <linux/tty.h>
158c2ecf20Sopenharmony_ci#include <linux/mm.h>		/* __get_free_page() and friends */
168c2ecf20Sopenharmony_ci#include <linux/vt_kern.h>
178c2ecf20Sopenharmony_ci#include <linux/ctype.h>
188c2ecf20Sopenharmony_ci#include <linux/selection.h>
198c2ecf20Sopenharmony_ci#include <linux/unistd.h>
208c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
218c2ecf20Sopenharmony_ci#include <linux/kthread.h>
228c2ecf20Sopenharmony_ci#include <linux/keyboard.h>	/* for KT_SHIFT */
238c2ecf20Sopenharmony_ci#include <linux/kbd_kern.h>	/* for vc_kbd_* and friends */
248c2ecf20Sopenharmony_ci#include <linux/input.h>
258c2ecf20Sopenharmony_ci#include <linux/kmod.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* speakup_*_selection */
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/sched.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci#include <linux/types.h>
328c2ecf20Sopenharmony_ci#include <linux/consolemap.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
358c2ecf20Sopenharmony_ci#include <linux/notifier.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <linux/uaccess.h>	/* copy_from|to|user() and others */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include "spk_priv.h"
408c2ecf20Sopenharmony_ci#include "speakup.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define MAX_DELAY msecs_to_jiffies(500)
438c2ecf20Sopenharmony_ci#define MINECHOCHAR SPACE
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Speakup console speech");
488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
498c2ecf20Sopenharmony_ciMODULE_VERSION(SPEAKUP_VERSION);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cichar *synth_name;
528c2ecf20Sopenharmony_cimodule_param_named(synth, synth_name, charp, 0444);
538c2ecf20Sopenharmony_cimodule_param_named(quiet, spk_quiet_boot, bool, 0444);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cispecial_func spk_special_handler;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cishort spk_pitch_shift, synth_flags;
618c2ecf20Sopenharmony_cistatic u16 buf[256];
628c2ecf20Sopenharmony_ciint spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
638c2ecf20Sopenharmony_ciint spk_no_intr, spk_spell_delay;
648c2ecf20Sopenharmony_ciint spk_key_echo, spk_say_word_ctl;
658c2ecf20Sopenharmony_ciint spk_say_ctrl, spk_bell_pos;
668c2ecf20Sopenharmony_cishort spk_punc_mask;
678c2ecf20Sopenharmony_ciint spk_punc_level, spk_reading_punc;
688c2ecf20Sopenharmony_cichar spk_str_caps_start[MAXVARLEN + 1] = "\0";
698c2ecf20Sopenharmony_cichar spk_str_caps_stop[MAXVARLEN + 1] = "\0";
708c2ecf20Sopenharmony_cichar spk_str_pause[MAXVARLEN + 1] = "\0";
718c2ecf20Sopenharmony_cibool spk_paused;
728c2ecf20Sopenharmony_ciconst struct st_bits_data spk_punc_info[] = {
738c2ecf20Sopenharmony_ci	{"none", "", 0},
748c2ecf20Sopenharmony_ci	{"some", "/$%&@", SOME},
758c2ecf20Sopenharmony_ci	{"most", "$%&#()=+*/@^<>|\\", MOST},
768c2ecf20Sopenharmony_ci	{"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
778c2ecf20Sopenharmony_ci	{"delimiters", "", B_WDLM},
788c2ecf20Sopenharmony_ci	{"repeats", "()", CH_RPT},
798c2ecf20Sopenharmony_ci	{"extended numeric", "", B_EXNUM},
808c2ecf20Sopenharmony_ci	{"symbols", "", B_SYM},
818c2ecf20Sopenharmony_ci	{NULL, NULL}
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic char mark_cut_flag;
858c2ecf20Sopenharmony_ci#define MAX_KEY 160
868c2ecf20Sopenharmony_cistatic u_char *spk_shift_table;
878c2ecf20Sopenharmony_ciu_char *spk_our_keys[MAX_KEY];
888c2ecf20Sopenharmony_ciu_char spk_key_buf[600];
898c2ecf20Sopenharmony_ciconst u_char spk_key_defaults[] = {
908c2ecf20Sopenharmony_ci#include "speakupmap.h"
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* Speakup Cursor Track Variables */
948c2ecf20Sopenharmony_cistatic int cursor_track = 1, prev_cursor_track = 1;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* cursor track modes, must be ordered same as cursor_msgs */
978c2ecf20Sopenharmony_cienum {
988c2ecf20Sopenharmony_ci	CT_Off = 0,
998c2ecf20Sopenharmony_ci	CT_On,
1008c2ecf20Sopenharmony_ci	CT_Highlight,
1018c2ecf20Sopenharmony_ci	CT_Window,
1028c2ecf20Sopenharmony_ci	CT_Max
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci#define read_all_mode CT_Max
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic struct tty_struct *tty;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void spkup_write(const u16 *in_buf, int count);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic char *phonetic[] = {
1128c2ecf20Sopenharmony_ci	"alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
1138c2ecf20Sopenharmony_ci	"india", "juliett", "keelo", "leema", "mike", "november", "oscar",
1148c2ecf20Sopenharmony_ci	    "papa",
1158c2ecf20Sopenharmony_ci	"keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
1168c2ecf20Sopenharmony_ci	"x ray", "yankee", "zulu"
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* array of 256 char pointers (one for each character description)
1208c2ecf20Sopenharmony_ci * initialized to default_chars and user selectable via
1218c2ecf20Sopenharmony_ci * /proc/speakup/characters
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_cichar *spk_characters[256];
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cichar *spk_default_chars[256] = {
1268c2ecf20Sopenharmony_ci/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
1278c2ecf20Sopenharmony_ci/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
1288c2ecf20Sopenharmony_ci/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
1298c2ecf20Sopenharmony_ci/*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
1308c2ecf20Sopenharmony_ci	    "control",
1318c2ecf20Sopenharmony_ci/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
1328c2ecf20Sopenharmony_ci	    "tick",
1338c2ecf20Sopenharmony_ci/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
1348c2ecf20Sopenharmony_ci	    "dot",
1358c2ecf20Sopenharmony_ci	"slash",
1368c2ecf20Sopenharmony_ci/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
1378c2ecf20Sopenharmony_ci	"eight", "nine",
1388c2ecf20Sopenharmony_ci/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
1398c2ecf20Sopenharmony_ci/*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
1408c2ecf20Sopenharmony_ci/*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
1418c2ecf20Sopenharmony_ci/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
1428c2ecf20Sopenharmony_ci/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
1438c2ecf20Sopenharmony_ci	    "caret",
1448c2ecf20Sopenharmony_ci	"line",
1458c2ecf20Sopenharmony_ci/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
1468c2ecf20Sopenharmony_ci/*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
1478c2ecf20Sopenharmony_ci/*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
1488c2ecf20Sopenharmony_ci/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
1498c2ecf20Sopenharmony_ci/*127*/ "del", "control", "control", "control", "control", "control",
1508c2ecf20Sopenharmony_ci	    "control", "control", "control", "control", "control",
1518c2ecf20Sopenharmony_ci/*138*/ "control", "control", "control", "control", "control",
1528c2ecf20Sopenharmony_ci	    "control", "control", "control", "control", "control",
1538c2ecf20Sopenharmony_ci	    "control", "control",
1548c2ecf20Sopenharmony_ci/*150*/ "control", "control", "control", "control", "control",
1558c2ecf20Sopenharmony_ci	    "control", "control", "control", "control", "control",
1568c2ecf20Sopenharmony_ci/*160*/ "nbsp", "inverted bang",
1578c2ecf20Sopenharmony_ci/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
1588c2ecf20Sopenharmony_ci/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
1598c2ecf20Sopenharmony_ci/*172*/ "not", "soft hyphen", "registered", "macron",
1608c2ecf20Sopenharmony_ci/*176*/ "degrees", "plus or minus", "super two", "super three",
1618c2ecf20Sopenharmony_ci/*180*/ "acute accent", "micro", "pilcrow", "middle dot",
1628c2ecf20Sopenharmony_ci/*184*/ "cedilla", "super one", "male ordinal", "double right angle",
1638c2ecf20Sopenharmony_ci/*188*/ "one quarter", "one half", "three quarters",
1648c2ecf20Sopenharmony_ci	    "inverted question",
1658c2ecf20Sopenharmony_ci/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
1668c2ecf20Sopenharmony_ci	    "A RING",
1678c2ecf20Sopenharmony_ci/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
1688c2ecf20Sopenharmony_ci	    "E OOMLAUT",
1698c2ecf20Sopenharmony_ci/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
1708c2ecf20Sopenharmony_ci	    "N TILDE",
1718c2ecf20Sopenharmony_ci/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
1728c2ecf20Sopenharmony_ci/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
1738c2ecf20Sopenharmony_ci	    "U CIRCUMFLEX",
1748c2ecf20Sopenharmony_ci/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
1758c2ecf20Sopenharmony_ci/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
1768c2ecf20Sopenharmony_ci/*230*/ "ae", "c cidella", "e grave", "e acute",
1778c2ecf20Sopenharmony_ci/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
1788c2ecf20Sopenharmony_ci	    "i circumflex",
1798c2ecf20Sopenharmony_ci/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
1808c2ecf20Sopenharmony_ci	    "o circumflex",
1818c2ecf20Sopenharmony_ci/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
1828c2ecf20Sopenharmony_ci	    "u acute",
1838c2ecf20Sopenharmony_ci/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/* array of 256 u_short (one for each character)
1878c2ecf20Sopenharmony_ci * initialized to default_chartab and user selectable via
1888c2ecf20Sopenharmony_ci * /sys/module/speakup/parameters/chartab
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_ciu_short spk_chartab[256];
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic u_short default_chartab[256] = {
1938c2ecf20Sopenharmony_ci	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 0-7 */
1948c2ecf20Sopenharmony_ci	B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 8-15 */
1958c2ecf20Sopenharmony_ci	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/*16-23 */
1968c2ecf20Sopenharmony_ci	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 24-31 */
1978c2ecf20Sopenharmony_ci	WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,	/*  !"#$%&' */
1988c2ecf20Sopenharmony_ci	PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC,	/* ()*+, -./ */
1998c2ecf20Sopenharmony_ci	NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM,	/* 01234567 */
2008c2ecf20Sopenharmony_ci	NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,	/* 89:;<=>? */
2018c2ecf20Sopenharmony_ci	PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* @ABCDEFG */
2028c2ecf20Sopenharmony_ci	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* HIJKLMNO */
2038c2ecf20Sopenharmony_ci	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* PQRSTUVW */
2048c2ecf20Sopenharmony_ci	A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC,	/* XYZ[\]^_ */
2058c2ecf20Sopenharmony_ci	PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* `abcdefg */
2068c2ecf20Sopenharmony_ci	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* hijklmno */
2078c2ecf20Sopenharmony_ci	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* pqrstuvw */
2088c2ecf20Sopenharmony_ci	ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0,	/* xyz{|}~ */
2098c2ecf20Sopenharmony_ci	B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
2108c2ecf20Sopenharmony_ci	B_SYM,	/* 135 */
2118c2ecf20Sopenharmony_ci	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
2128c2ecf20Sopenharmony_ci	B_CAPSYM,	/* 143 */
2138c2ecf20Sopenharmony_ci	B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
2148c2ecf20Sopenharmony_ci	B_SYM,	/* 151 */
2158c2ecf20Sopenharmony_ci	B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
2168c2ecf20Sopenharmony_ci	B_SYM,	/* 159 */
2178c2ecf20Sopenharmony_ci	WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
2188c2ecf20Sopenharmony_ci	B_SYM,	/* 167 */
2198c2ecf20Sopenharmony_ci	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 168-175 */
2208c2ecf20Sopenharmony_ci	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 176-183 */
2218c2ecf20Sopenharmony_ci	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 184-191 */
2228c2ecf20Sopenharmony_ci	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* 192-199 */
2238c2ecf20Sopenharmony_ci	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* 200-207 */
2248c2ecf20Sopenharmony_ci	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM,	/* 208-215 */
2258c2ecf20Sopenharmony_ci	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA,	/* 216-223 */
2268c2ecf20Sopenharmony_ci	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* 224-231 */
2278c2ecf20Sopenharmony_ci	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* 232-239 */
2288c2ecf20Sopenharmony_ci	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM,	/* 240-247 */
2298c2ecf20Sopenharmony_ci	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA	/* 248-255 */
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistruct task_struct *speakup_task;
2338c2ecf20Sopenharmony_cistruct bleep spk_unprocessed_sound;
2348c2ecf20Sopenharmony_cistatic int spk_keydown;
2358c2ecf20Sopenharmony_cistatic u16 spk_lastkey;
2368c2ecf20Sopenharmony_cistatic u_char spk_close_press, keymap_flags;
2378c2ecf20Sopenharmony_cistatic u_char last_keycode, this_speakup_key;
2388c2ecf20Sopenharmony_cistatic u_long last_spk_jiffy;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistruct st_spk_t *speakup_console[MAX_NR_CONSOLES];
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ciDEFINE_MUTEX(spk_mutex);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int keyboard_notifier_call(struct notifier_block *,
2458c2ecf20Sopenharmony_ci				  unsigned long code, void *param);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic struct notifier_block keyboard_notifier_block = {
2488c2ecf20Sopenharmony_ci	.notifier_call = keyboard_notifier_call,
2498c2ecf20Sopenharmony_ci};
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int vt_notifier_call(struct notifier_block *,
2528c2ecf20Sopenharmony_ci			    unsigned long code, void *param);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic struct notifier_block vt_notifier_block = {
2558c2ecf20Sopenharmony_ci	.notifier_call = vt_notifier_call,
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic unsigned char get_attributes(struct vc_data *vc, u16 *pos)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
2618c2ecf20Sopenharmony_ci	return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void speakup_date(struct vc_data *vc)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	spk_x = spk_cx = vc->state.x;
2678c2ecf20Sopenharmony_ci	spk_y = spk_cy = vc->state.y;
2688c2ecf20Sopenharmony_ci	spk_pos = spk_cp = vc->vc_pos;
2698c2ecf20Sopenharmony_ci	spk_old_attr = spk_attr;
2708c2ecf20Sopenharmony_ci	spk_attr = get_attributes(vc, (u_short *)spk_pos);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void bleep(u_short val)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	static const short vals[] = {
2768c2ecf20Sopenharmony_ci		350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
2778c2ecf20Sopenharmony_ci	};
2788c2ecf20Sopenharmony_ci	short freq;
2798c2ecf20Sopenharmony_ci	int time = spk_bleep_time;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	freq = vals[val % 12];
2828c2ecf20Sopenharmony_ci	if (val > 11)
2838c2ecf20Sopenharmony_ci		freq *= (1 << (val / 12));
2848c2ecf20Sopenharmony_ci	spk_unprocessed_sound.freq = freq;
2858c2ecf20Sopenharmony_ci	spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
2868c2ecf20Sopenharmony_ci	spk_unprocessed_sound.active = 1;
2878c2ecf20Sopenharmony_ci	/* We can only have 1 active sound at a time. */
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void speakup_shut_up(struct vc_data *vc)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	if (spk_killed)
2938c2ecf20Sopenharmony_ci		return;
2948c2ecf20Sopenharmony_ci	spk_shut_up |= 0x01;
2958c2ecf20Sopenharmony_ci	spk_parked &= 0xfe;
2968c2ecf20Sopenharmony_ci	speakup_date(vc);
2978c2ecf20Sopenharmony_ci	if (synth)
2988c2ecf20Sopenharmony_ci		spk_do_flush();
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void speech_kill(struct vc_data *vc)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	char val = synth->is_alive(synth);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (val == 0)
3068c2ecf20Sopenharmony_ci		return;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* re-enables synth, if disabled */
3098c2ecf20Sopenharmony_ci	if (val == 2 || spk_killed) {
3108c2ecf20Sopenharmony_ci		/* dead */
3118c2ecf20Sopenharmony_ci		spk_shut_up &= ~0x40;
3128c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
3138c2ecf20Sopenharmony_ci	} else {
3148c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
3158c2ecf20Sopenharmony_ci		spk_shut_up |= 0x40;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic void speakup_off(struct vc_data *vc)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	if (spk_shut_up & 0x80) {
3228c2ecf20Sopenharmony_ci		spk_shut_up &= 0x7f;
3238c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
3248c2ecf20Sopenharmony_ci	} else {
3258c2ecf20Sopenharmony_ci		spk_shut_up |= 0x80;
3268c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci	speakup_date(vc);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void speakup_parked(struct vc_data *vc)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	if (spk_parked & 0x80) {
3348c2ecf20Sopenharmony_ci		spk_parked = 0;
3358c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
3368c2ecf20Sopenharmony_ci	} else {
3378c2ecf20Sopenharmony_ci		spk_parked |= 0x80;
3388c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_PARKED));
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic void speakup_cut(struct vc_data *vc)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	static const char err_buf[] = "set selection failed";
3458c2ecf20Sopenharmony_ci	int ret;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!mark_cut_flag) {
3488c2ecf20Sopenharmony_ci		mark_cut_flag = 1;
3498c2ecf20Sopenharmony_ci		spk_xs = (u_short)spk_x;
3508c2ecf20Sopenharmony_ci		spk_ys = (u_short)spk_y;
3518c2ecf20Sopenharmony_ci		spk_sel_cons = vc;
3528c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_MARK));
3538c2ecf20Sopenharmony_ci		return;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	spk_xe = (u_short)spk_x;
3568c2ecf20Sopenharmony_ci	spk_ye = (u_short)spk_y;
3578c2ecf20Sopenharmony_ci	mark_cut_flag = 0;
3588c2ecf20Sopenharmony_ci	synth_printf("%s\n", spk_msg_get(MSG_CUT));
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	ret = speakup_set_selection(tty);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	switch (ret) {
3638c2ecf20Sopenharmony_ci	case 0:
3648c2ecf20Sopenharmony_ci		break;		/* no error */
3658c2ecf20Sopenharmony_ci	case -EFAULT:
3668c2ecf20Sopenharmony_ci		pr_warn("%sEFAULT\n", err_buf);
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	case -EINVAL:
3698c2ecf20Sopenharmony_ci		pr_warn("%sEINVAL\n", err_buf);
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci	case -ENOMEM:
3728c2ecf20Sopenharmony_ci		pr_warn("%sENOMEM\n", err_buf);
3738c2ecf20Sopenharmony_ci		break;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic void speakup_paste(struct vc_data *vc)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	if (mark_cut_flag) {
3808c2ecf20Sopenharmony_ci		mark_cut_flag = 0;
3818c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
3828c2ecf20Sopenharmony_ci	} else {
3838c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_PASTE));
3848c2ecf20Sopenharmony_ci		speakup_paste_selection(tty);
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void say_attributes(struct vc_data *vc)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	int fg = spk_attr & 0x0f;
3918c2ecf20Sopenharmony_ci	int bg = spk_attr >> 4;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (fg > 8) {
3948c2ecf20Sopenharmony_ci		synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
3958c2ecf20Sopenharmony_ci		fg -= 8;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
3988c2ecf20Sopenharmony_ci	if (bg > 7) {
3998c2ecf20Sopenharmony_ci		synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
4008c2ecf20Sopenharmony_ci		bg -= 8;
4018c2ecf20Sopenharmony_ci	} else {
4028c2ecf20Sopenharmony_ci		synth_printf(" %s ", spk_msg_get(MSG_ON));
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci	synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cienum {
4088c2ecf20Sopenharmony_ci	edge_top = 1,
4098c2ecf20Sopenharmony_ci	edge_bottom,
4108c2ecf20Sopenharmony_ci	edge_left,
4118c2ecf20Sopenharmony_ci	edge_right,
4128c2ecf20Sopenharmony_ci	edge_quiet
4138c2ecf20Sopenharmony_ci};
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic void announce_edge(struct vc_data *vc, int msg_id)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	if (spk_bleeps & 1)
4188c2ecf20Sopenharmony_ci		bleep(spk_y);
4198c2ecf20Sopenharmony_ci	if ((spk_bleeps & 2) && (msg_id < edge_quiet))
4208c2ecf20Sopenharmony_ci		synth_printf("%s\n",
4218c2ecf20Sopenharmony_ci			     spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic void speak_char(u16 ch)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	char *cp;
4278c2ecf20Sopenharmony_ci	struct var_t *direct = spk_get_var(DIRECT);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (ch >= 0x100 || (direct && direct->u.n.value)) {
4308c2ecf20Sopenharmony_ci		if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
4318c2ecf20Sopenharmony_ci			spk_pitch_shift++;
4328c2ecf20Sopenharmony_ci			synth_printf("%s", spk_str_caps_start);
4338c2ecf20Sopenharmony_ci		}
4348c2ecf20Sopenharmony_ci		synth_putwc_s(ch);
4358c2ecf20Sopenharmony_ci		if (ch < 0x100 && IS_CHAR(ch, B_CAP))
4368c2ecf20Sopenharmony_ci			synth_printf("%s", spk_str_caps_stop);
4378c2ecf20Sopenharmony_ci		return;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	cp = spk_characters[ch];
4418c2ecf20Sopenharmony_ci	if (!cp) {
4428c2ecf20Sopenharmony_ci		pr_info("%s: cp == NULL!\n", __func__);
4438c2ecf20Sopenharmony_ci		return;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci	if (IS_CHAR(ch, B_CAP)) {
4468c2ecf20Sopenharmony_ci		spk_pitch_shift++;
4478c2ecf20Sopenharmony_ci		synth_printf("%s %s %s",
4488c2ecf20Sopenharmony_ci			     spk_str_caps_start, cp, spk_str_caps_stop);
4498c2ecf20Sopenharmony_ci	} else {
4508c2ecf20Sopenharmony_ci		if (*cp == '^') {
4518c2ecf20Sopenharmony_ci			cp++;
4528c2ecf20Sopenharmony_ci			synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);
4538c2ecf20Sopenharmony_ci		} else {
4548c2ecf20Sopenharmony_ci			synth_printf(" %s ", cp);
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	u16 ch = ' ';
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if (vc && pos) {
4648c2ecf20Sopenharmony_ci		u16 w;
4658c2ecf20Sopenharmony_ci		u16 c;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
4688c2ecf20Sopenharmony_ci		w = scr_readw(pos);
4698c2ecf20Sopenharmony_ci		c = w & 0xff;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		if (w & vc->vc_hi_font_mask) {
4728c2ecf20Sopenharmony_ci			w &= ~vc->vc_hi_font_mask;
4738c2ecf20Sopenharmony_ci			c |= 0x100;
4748c2ecf20Sopenharmony_ci		}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		ch = inverse_translate(vc, c, 1);
4778c2ecf20Sopenharmony_ci		*attribs = (w & 0xff00) >> 8;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci	return ch;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic void say_char(struct vc_data *vc)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	u16 ch;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	spk_old_attr = spk_attr;
4878c2ecf20Sopenharmony_ci	ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
4888c2ecf20Sopenharmony_ci	if (spk_attr != spk_old_attr) {
4898c2ecf20Sopenharmony_ci		if (spk_attrib_bleep & 1)
4908c2ecf20Sopenharmony_ci			bleep(spk_y);
4918c2ecf20Sopenharmony_ci		if (spk_attrib_bleep & 2)
4928c2ecf20Sopenharmony_ci			say_attributes(vc);
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	speak_char(ch);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic void say_phonetic_char(struct vc_data *vc)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	u16 ch;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	spk_old_attr = spk_attr;
5028c2ecf20Sopenharmony_ci	ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
5038c2ecf20Sopenharmony_ci	if (ch <= 0x7f && isalpha(ch)) {
5048c2ecf20Sopenharmony_ci		ch &= 0x1f;
5058c2ecf20Sopenharmony_ci		synth_printf("%s\n", phonetic[--ch]);
5068c2ecf20Sopenharmony_ci	} else {
5078c2ecf20Sopenharmony_ci		if (ch < 0x100 && IS_CHAR(ch, B_NUM))
5088c2ecf20Sopenharmony_ci			synth_printf("%s ", spk_msg_get(MSG_NUMBER));
5098c2ecf20Sopenharmony_ci		speak_char(ch);
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic void say_prev_char(struct vc_data *vc)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
5168c2ecf20Sopenharmony_ci	if (spk_x == 0) {
5178c2ecf20Sopenharmony_ci		announce_edge(vc, edge_left);
5188c2ecf20Sopenharmony_ci		return;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	spk_x--;
5218c2ecf20Sopenharmony_ci	spk_pos -= 2;
5228c2ecf20Sopenharmony_ci	say_char(vc);
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic void say_next_char(struct vc_data *vc)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
5288c2ecf20Sopenharmony_ci	if (spk_x == vc->vc_cols - 1) {
5298c2ecf20Sopenharmony_ci		announce_edge(vc, edge_right);
5308c2ecf20Sopenharmony_ci		return;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	spk_x++;
5338c2ecf20Sopenharmony_ci	spk_pos += 2;
5348c2ecf20Sopenharmony_ci	say_char(vc);
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci/* get_word - will first check to see if the character under the
5388c2ecf20Sopenharmony_ci * reading cursor is a space and if spk_say_word_ctl is true it will
5398c2ecf20Sopenharmony_ci * return the word space.  If spk_say_word_ctl is not set it will check to
5408c2ecf20Sopenharmony_ci * see if there is a word starting on the next position to the right
5418c2ecf20Sopenharmony_ci * and return that word if it exists.  If it does not exist it will
5428c2ecf20Sopenharmony_ci * move left to the beginning of any previous word on the line or the
5438c2ecf20Sopenharmony_ci * beginning off the line whichever comes first..
5448c2ecf20Sopenharmony_ci */
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic u_long get_word(struct vc_data *vc)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
5498c2ecf20Sopenharmony_ci	u16 ch;
5508c2ecf20Sopenharmony_ci	u16 attr_ch;
5518c2ecf20Sopenharmony_ci	u_char temp;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	spk_old_attr = spk_attr;
5548c2ecf20Sopenharmony_ci	ch = get_char(vc, (u_short *)tmp_pos, &temp);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/* decided to take out the sayword if on a space (mis-information */
5578c2ecf20Sopenharmony_ci	if (spk_say_word_ctl && ch == SPACE) {
5588c2ecf20Sopenharmony_ci		*buf = '\0';
5598c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_SPACE));
5608c2ecf20Sopenharmony_ci		return 0;
5618c2ecf20Sopenharmony_ci	} else if (tmpx < vc->vc_cols - 2 &&
5628c2ecf20Sopenharmony_ci		   (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
5638c2ecf20Sopenharmony_ci		   get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
5648c2ecf20Sopenharmony_ci		tmp_pos += 2;
5658c2ecf20Sopenharmony_ci		tmpx++;
5668c2ecf20Sopenharmony_ci	} else {
5678c2ecf20Sopenharmony_ci		while (tmpx > 0) {
5688c2ecf20Sopenharmony_ci			ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
5698c2ecf20Sopenharmony_ci			if ((ch == SPACE || ch == 0 ||
5708c2ecf20Sopenharmony_ci			     (ch < 0x100 && IS_WDLM(ch))) &&
5718c2ecf20Sopenharmony_ci			    get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
5728c2ecf20Sopenharmony_ci				break;
5738c2ecf20Sopenharmony_ci			tmp_pos -= 2;
5748c2ecf20Sopenharmony_ci			tmpx--;
5758c2ecf20Sopenharmony_ci		}
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci	attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
5788c2ecf20Sopenharmony_ci	buf[cnt++] = attr_ch;
5798c2ecf20Sopenharmony_ci	while (tmpx < vc->vc_cols - 1) {
5808c2ecf20Sopenharmony_ci		tmp_pos += 2;
5818c2ecf20Sopenharmony_ci		tmpx++;
5828c2ecf20Sopenharmony_ci		ch = get_char(vc, (u_short *)tmp_pos, &temp);
5838c2ecf20Sopenharmony_ci		if (ch == SPACE || ch == 0 ||
5848c2ecf20Sopenharmony_ci		    (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
5858c2ecf20Sopenharmony_ci		     ch > SPACE))
5868c2ecf20Sopenharmony_ci			break;
5878c2ecf20Sopenharmony_ci		buf[cnt++] = ch;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci	buf[cnt] = '\0';
5908c2ecf20Sopenharmony_ci	return cnt;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic void say_word(struct vc_data *vc)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	u_long cnt = get_word(vc);
5968c2ecf20Sopenharmony_ci	u_short saved_punc_mask = spk_punc_mask;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (cnt == 0)
5998c2ecf20Sopenharmony_ci		return;
6008c2ecf20Sopenharmony_ci	spk_punc_mask = PUNC;
6018c2ecf20Sopenharmony_ci	buf[cnt++] = SPACE;
6028c2ecf20Sopenharmony_ci	spkup_write(buf, cnt);
6038c2ecf20Sopenharmony_ci	spk_punc_mask = saved_punc_mask;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic void say_prev_word(struct vc_data *vc)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	u_char temp;
6098c2ecf20Sopenharmony_ci	u16 ch;
6108c2ecf20Sopenharmony_ci	u_short edge_said = 0, last_state = 0, state = 0;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (spk_x == 0) {
6158c2ecf20Sopenharmony_ci		if (spk_y == 0) {
6168c2ecf20Sopenharmony_ci			announce_edge(vc, edge_top);
6178c2ecf20Sopenharmony_ci			return;
6188c2ecf20Sopenharmony_ci		}
6198c2ecf20Sopenharmony_ci		spk_y--;
6208c2ecf20Sopenharmony_ci		spk_x = vc->vc_cols;
6218c2ecf20Sopenharmony_ci		edge_said = edge_quiet;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci	while (1) {
6248c2ecf20Sopenharmony_ci		if (spk_x == 0) {
6258c2ecf20Sopenharmony_ci			if (spk_y == 0) {
6268c2ecf20Sopenharmony_ci				edge_said = edge_top;
6278c2ecf20Sopenharmony_ci				break;
6288c2ecf20Sopenharmony_ci			}
6298c2ecf20Sopenharmony_ci			if (edge_said != edge_quiet)
6308c2ecf20Sopenharmony_ci				edge_said = edge_left;
6318c2ecf20Sopenharmony_ci			if (state > 0)
6328c2ecf20Sopenharmony_ci				break;
6338c2ecf20Sopenharmony_ci			spk_y--;
6348c2ecf20Sopenharmony_ci			spk_x = vc->vc_cols - 1;
6358c2ecf20Sopenharmony_ci		} else {
6368c2ecf20Sopenharmony_ci			spk_x--;
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci		spk_pos -= 2;
6398c2ecf20Sopenharmony_ci		ch = get_char(vc, (u_short *)spk_pos, &temp);
6408c2ecf20Sopenharmony_ci		if (ch == SPACE || ch == 0)
6418c2ecf20Sopenharmony_ci			state = 0;
6428c2ecf20Sopenharmony_ci		else if (ch < 0x100 && IS_WDLM(ch))
6438c2ecf20Sopenharmony_ci			state = 1;
6448c2ecf20Sopenharmony_ci		else
6458c2ecf20Sopenharmony_ci			state = 2;
6468c2ecf20Sopenharmony_ci		if (state < last_state) {
6478c2ecf20Sopenharmony_ci			spk_pos += 2;
6488c2ecf20Sopenharmony_ci			spk_x++;
6498c2ecf20Sopenharmony_ci			break;
6508c2ecf20Sopenharmony_ci		}
6518c2ecf20Sopenharmony_ci		last_state = state;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci	if (spk_x == 0 && edge_said == edge_quiet)
6548c2ecf20Sopenharmony_ci		edge_said = edge_left;
6558c2ecf20Sopenharmony_ci	if (edge_said > 0 && edge_said < edge_quiet)
6568c2ecf20Sopenharmony_ci		announce_edge(vc, edge_said);
6578c2ecf20Sopenharmony_ci	say_word(vc);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic void say_next_word(struct vc_data *vc)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	u_char temp;
6638c2ecf20Sopenharmony_ci	u16 ch;
6648c2ecf20Sopenharmony_ci	u_short edge_said = 0, last_state = 2, state = 0;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
6678c2ecf20Sopenharmony_ci	if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
6688c2ecf20Sopenharmony_ci		announce_edge(vc, edge_bottom);
6698c2ecf20Sopenharmony_ci		return;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci	while (1) {
6728c2ecf20Sopenharmony_ci		ch = get_char(vc, (u_short *)spk_pos, &temp);
6738c2ecf20Sopenharmony_ci		if (ch == SPACE || ch == 0)
6748c2ecf20Sopenharmony_ci			state = 0;
6758c2ecf20Sopenharmony_ci		else if (ch < 0x100 && IS_WDLM(ch))
6768c2ecf20Sopenharmony_ci			state = 1;
6778c2ecf20Sopenharmony_ci		else
6788c2ecf20Sopenharmony_ci			state = 2;
6798c2ecf20Sopenharmony_ci		if (state > last_state)
6808c2ecf20Sopenharmony_ci			break;
6818c2ecf20Sopenharmony_ci		if (spk_x >= vc->vc_cols - 1) {
6828c2ecf20Sopenharmony_ci			if (spk_y == vc->vc_rows - 1) {
6838c2ecf20Sopenharmony_ci				edge_said = edge_bottom;
6848c2ecf20Sopenharmony_ci				break;
6858c2ecf20Sopenharmony_ci			}
6868c2ecf20Sopenharmony_ci			state = 0;
6878c2ecf20Sopenharmony_ci			spk_y++;
6888c2ecf20Sopenharmony_ci			spk_x = 0;
6898c2ecf20Sopenharmony_ci			edge_said = edge_right;
6908c2ecf20Sopenharmony_ci		} else {
6918c2ecf20Sopenharmony_ci			spk_x++;
6928c2ecf20Sopenharmony_ci		}
6938c2ecf20Sopenharmony_ci		spk_pos += 2;
6948c2ecf20Sopenharmony_ci		last_state = state;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci	if (edge_said > 0)
6978c2ecf20Sopenharmony_ci		announce_edge(vc, edge_said);
6988c2ecf20Sopenharmony_ci	say_word(vc);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic void spell_word(struct vc_data *vc)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
7048c2ecf20Sopenharmony_ci	u16 *cp = buf;
7058c2ecf20Sopenharmony_ci	char *cp1;
7068c2ecf20Sopenharmony_ci	char *str_cap = spk_str_caps_stop;
7078c2ecf20Sopenharmony_ci	char *last_cap = spk_str_caps_stop;
7088c2ecf20Sopenharmony_ci	struct var_t *direct = spk_get_var(DIRECT);
7098c2ecf20Sopenharmony_ci	u16 ch;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (!get_word(vc))
7128c2ecf20Sopenharmony_ci		return;
7138c2ecf20Sopenharmony_ci	while ((ch = *cp)) {
7148c2ecf20Sopenharmony_ci		if (cp != buf)
7158c2ecf20Sopenharmony_ci			synth_printf(" %s ", delay_str[spk_spell_delay]);
7168c2ecf20Sopenharmony_ci		/* FIXME: Non-latin1 considered as lower case */
7178c2ecf20Sopenharmony_ci		if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
7188c2ecf20Sopenharmony_ci			str_cap = spk_str_caps_start;
7198c2ecf20Sopenharmony_ci			if (*spk_str_caps_stop)
7208c2ecf20Sopenharmony_ci				spk_pitch_shift++;
7218c2ecf20Sopenharmony_ci			else	/* synth has no pitch */
7228c2ecf20Sopenharmony_ci				last_cap = spk_str_caps_stop;
7238c2ecf20Sopenharmony_ci		} else {
7248c2ecf20Sopenharmony_ci			str_cap = spk_str_caps_stop;
7258c2ecf20Sopenharmony_ci		}
7268c2ecf20Sopenharmony_ci		if (str_cap != last_cap) {
7278c2ecf20Sopenharmony_ci			synth_printf("%s", str_cap);
7288c2ecf20Sopenharmony_ci			last_cap = str_cap;
7298c2ecf20Sopenharmony_ci		}
7308c2ecf20Sopenharmony_ci		if (ch >= 0x100 || (direct && direct->u.n.value)) {
7318c2ecf20Sopenharmony_ci			synth_putwc_s(ch);
7328c2ecf20Sopenharmony_ci		} else if (this_speakup_key == SPELL_PHONETIC &&
7338c2ecf20Sopenharmony_ci		    ch <= 0x7f && isalpha(ch)) {
7348c2ecf20Sopenharmony_ci			ch &= 0x1f;
7358c2ecf20Sopenharmony_ci			cp1 = phonetic[--ch];
7368c2ecf20Sopenharmony_ci			synth_printf("%s", cp1);
7378c2ecf20Sopenharmony_ci		} else {
7388c2ecf20Sopenharmony_ci			cp1 = spk_characters[ch];
7398c2ecf20Sopenharmony_ci			if (*cp1 == '^') {
7408c2ecf20Sopenharmony_ci				synth_printf("%s", spk_msg_get(MSG_CTRL));
7418c2ecf20Sopenharmony_ci				cp1++;
7428c2ecf20Sopenharmony_ci			}
7438c2ecf20Sopenharmony_ci			synth_printf("%s", cp1);
7448c2ecf20Sopenharmony_ci		}
7458c2ecf20Sopenharmony_ci		cp++;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci	if (str_cap != spk_str_caps_stop)
7488c2ecf20Sopenharmony_ci		synth_printf("%s", spk_str_caps_stop);
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic int get_line(struct vc_data *vc)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	u_long tmp = spk_pos - (spk_x * 2);
7548c2ecf20Sopenharmony_ci	int i = 0;
7558c2ecf20Sopenharmony_ci	u_char tmp2;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	spk_old_attr = spk_attr;
7588c2ecf20Sopenharmony_ci	spk_attr = get_attributes(vc, (u_short *)spk_pos);
7598c2ecf20Sopenharmony_ci	for (i = 0; i < vc->vc_cols; i++) {
7608c2ecf20Sopenharmony_ci		buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
7618c2ecf20Sopenharmony_ci		tmp += 2;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci	for (--i; i >= 0; i--)
7648c2ecf20Sopenharmony_ci		if (buf[i] != SPACE)
7658c2ecf20Sopenharmony_ci			break;
7668c2ecf20Sopenharmony_ci	return ++i;
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic void say_line(struct vc_data *vc)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	int i = get_line(vc);
7728c2ecf20Sopenharmony_ci	u16 *cp;
7738c2ecf20Sopenharmony_ci	u_short saved_punc_mask = spk_punc_mask;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	if (i == 0) {
7768c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_BLANK));
7778c2ecf20Sopenharmony_ci		return;
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci	buf[i++] = '\n';
7808c2ecf20Sopenharmony_ci	if (this_speakup_key == SAY_LINE_INDENT) {
7818c2ecf20Sopenharmony_ci		cp = buf;
7828c2ecf20Sopenharmony_ci		while (*cp == SPACE)
7838c2ecf20Sopenharmony_ci			cp++;
7848c2ecf20Sopenharmony_ci		synth_printf("%zd, ", (cp - buf) + 1);
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci	spk_punc_mask = spk_punc_masks[spk_reading_punc];
7878c2ecf20Sopenharmony_ci	spkup_write(buf, i);
7888c2ecf20Sopenharmony_ci	spk_punc_mask = saved_punc_mask;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic void say_prev_line(struct vc_data *vc)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
7948c2ecf20Sopenharmony_ci	if (spk_y == 0) {
7958c2ecf20Sopenharmony_ci		announce_edge(vc, edge_top);
7968c2ecf20Sopenharmony_ci		return;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci	spk_y--;
7998c2ecf20Sopenharmony_ci	spk_pos -= vc->vc_size_row;
8008c2ecf20Sopenharmony_ci	say_line(vc);
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_cistatic void say_next_line(struct vc_data *vc)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
8068c2ecf20Sopenharmony_ci	if (spk_y == vc->vc_rows - 1) {
8078c2ecf20Sopenharmony_ci		announce_edge(vc, edge_bottom);
8088c2ecf20Sopenharmony_ci		return;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci	spk_y++;
8118c2ecf20Sopenharmony_ci	spk_pos += vc->vc_size_row;
8128c2ecf20Sopenharmony_ci	say_line(vc);
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic int say_from_to(struct vc_data *vc, u_long from, u_long to,
8168c2ecf20Sopenharmony_ci		       int read_punc)
8178c2ecf20Sopenharmony_ci{
8188c2ecf20Sopenharmony_ci	int i = 0;
8198c2ecf20Sopenharmony_ci	u_char tmp;
8208c2ecf20Sopenharmony_ci	u_short saved_punc_mask = spk_punc_mask;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	spk_old_attr = spk_attr;
8238c2ecf20Sopenharmony_ci	spk_attr = get_attributes(vc, (u_short *)from);
8248c2ecf20Sopenharmony_ci	while (from < to) {
8258c2ecf20Sopenharmony_ci		buf[i++] = get_char(vc, (u_short *)from, &tmp);
8268c2ecf20Sopenharmony_ci		from += 2;
8278c2ecf20Sopenharmony_ci		if (i >= vc->vc_size_row)
8288c2ecf20Sopenharmony_ci			break;
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci	for (--i; i >= 0; i--)
8318c2ecf20Sopenharmony_ci		if (buf[i] != SPACE)
8328c2ecf20Sopenharmony_ci			break;
8338c2ecf20Sopenharmony_ci	buf[++i] = SPACE;
8348c2ecf20Sopenharmony_ci	buf[++i] = '\0';
8358c2ecf20Sopenharmony_ci	if (i < 1)
8368c2ecf20Sopenharmony_ci		return i;
8378c2ecf20Sopenharmony_ci	if (read_punc)
8388c2ecf20Sopenharmony_ci		spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
8398c2ecf20Sopenharmony_ci	spkup_write(buf, i);
8408c2ecf20Sopenharmony_ci	if (read_punc)
8418c2ecf20Sopenharmony_ci		spk_punc_mask = saved_punc_mask;
8428c2ecf20Sopenharmony_ci	return i - 1;
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
8468c2ecf20Sopenharmony_ci			     int read_punc)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
8498c2ecf20Sopenharmony_ci	u_long end = start + (to * 2);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	start += from * 2;
8528c2ecf20Sopenharmony_ci	if (say_from_to(vc, start, end, read_punc) <= 0)
8538c2ecf20Sopenharmony_ci		if (cursor_track != read_all_mode)
8548c2ecf20Sopenharmony_ci			synth_printf("%s\n", spk_msg_get(MSG_BLANK));
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci/* Sentence Reading Commands */
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int currsentence;
8608c2ecf20Sopenharmony_cistatic int numsentences[2];
8618c2ecf20Sopenharmony_cistatic u16 *sentbufend[2];
8628c2ecf20Sopenharmony_cistatic u16 *sentmarks[2][10];
8638c2ecf20Sopenharmony_cistatic int currbuf;
8648c2ecf20Sopenharmony_cistatic int bn;
8658c2ecf20Sopenharmony_cistatic u16 sentbuf[2][256];
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistatic int say_sentence_num(int num, int prev)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	bn = currbuf;
8708c2ecf20Sopenharmony_ci	currsentence = num + 1;
8718c2ecf20Sopenharmony_ci	if (prev && --bn == -1)
8728c2ecf20Sopenharmony_ci		bn = 1;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (num > numsentences[bn])
8758c2ecf20Sopenharmony_ci		return 0;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
8788c2ecf20Sopenharmony_ci	return 1;
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int get_sentence_buf(struct vc_data *vc, int read_punc)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	u_long start, end;
8848c2ecf20Sopenharmony_ci	int i, bn;
8858c2ecf20Sopenharmony_ci	u_char tmp;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	currbuf++;
8888c2ecf20Sopenharmony_ci	if (currbuf == 2)
8898c2ecf20Sopenharmony_ci		currbuf = 0;
8908c2ecf20Sopenharmony_ci	bn = currbuf;
8918c2ecf20Sopenharmony_ci	start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
8928c2ecf20Sopenharmony_ci	end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	numsentences[bn] = 0;
8958c2ecf20Sopenharmony_ci	sentmarks[bn][0] = &sentbuf[bn][0];
8968c2ecf20Sopenharmony_ci	i = 0;
8978c2ecf20Sopenharmony_ci	spk_old_attr = spk_attr;
8988c2ecf20Sopenharmony_ci	spk_attr = get_attributes(vc, (u_short *)start);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	while (start < end) {
9018c2ecf20Sopenharmony_ci		sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
9028c2ecf20Sopenharmony_ci		if (i > 0) {
9038c2ecf20Sopenharmony_ci			if (sentbuf[bn][i] == SPACE &&
9048c2ecf20Sopenharmony_ci			    sentbuf[bn][i - 1] == '.' &&
9058c2ecf20Sopenharmony_ci			    numsentences[bn] < 9) {
9068c2ecf20Sopenharmony_ci				/* Sentence Marker */
9078c2ecf20Sopenharmony_ci				numsentences[bn]++;
9088c2ecf20Sopenharmony_ci				sentmarks[bn][numsentences[bn]] =
9098c2ecf20Sopenharmony_ci				    &sentbuf[bn][i];
9108c2ecf20Sopenharmony_ci			}
9118c2ecf20Sopenharmony_ci		}
9128c2ecf20Sopenharmony_ci		i++;
9138c2ecf20Sopenharmony_ci		start += 2;
9148c2ecf20Sopenharmony_ci		if (i >= vc->vc_size_row)
9158c2ecf20Sopenharmony_ci			break;
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	for (--i; i >= 0; i--)
9198c2ecf20Sopenharmony_ci		if (sentbuf[bn][i] != SPACE)
9208c2ecf20Sopenharmony_ci			break;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (i < 1)
9238c2ecf20Sopenharmony_ci		return -1;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	sentbuf[bn][++i] = SPACE;
9268c2ecf20Sopenharmony_ci	sentbuf[bn][++i] = '\0';
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	sentbufend[bn] = &sentbuf[bn][i];
9298c2ecf20Sopenharmony_ci	return numsentences[bn];
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
9338c2ecf20Sopenharmony_ci{
9348c2ecf20Sopenharmony_ci	u_long start = vc->vc_origin, end;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	if (from > 0)
9378c2ecf20Sopenharmony_ci		start += from * vc->vc_size_row;
9388c2ecf20Sopenharmony_ci	if (to > vc->vc_rows)
9398c2ecf20Sopenharmony_ci		to = vc->vc_rows;
9408c2ecf20Sopenharmony_ci	end = vc->vc_origin + (to * vc->vc_size_row);
9418c2ecf20Sopenharmony_ci	for (from = start; from < end; from = to) {
9428c2ecf20Sopenharmony_ci		to = from + vc->vc_size_row;
9438c2ecf20Sopenharmony_ci		say_from_to(vc, from, to, 1);
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic void say_screen(struct vc_data *vc)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	say_screen_from_to(vc, 0, vc->vc_rows);
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic void speakup_win_say(struct vc_data *vc)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	u_long start, end, from, to;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	if (win_start < 2) {
9578c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
9588c2ecf20Sopenharmony_ci		return;
9598c2ecf20Sopenharmony_ci	}
9608c2ecf20Sopenharmony_ci	start = vc->vc_origin + (win_top * vc->vc_size_row);
9618c2ecf20Sopenharmony_ci	end = vc->vc_origin + (win_bottom * vc->vc_size_row);
9628c2ecf20Sopenharmony_ci	while (start <= end) {
9638c2ecf20Sopenharmony_ci		from = start + (win_left * 2);
9648c2ecf20Sopenharmony_ci		to = start + (win_right * 2);
9658c2ecf20Sopenharmony_ci		say_from_to(vc, from, to, 1);
9668c2ecf20Sopenharmony_ci		start += vc->vc_size_row;
9678c2ecf20Sopenharmony_ci	}
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic void top_edge(struct vc_data *vc)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
9738c2ecf20Sopenharmony_ci	spk_pos = vc->vc_origin + 2 * spk_x;
9748c2ecf20Sopenharmony_ci	spk_y = 0;
9758c2ecf20Sopenharmony_ci	say_line(vc);
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_cistatic void bottom_edge(struct vc_data *vc)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
9818c2ecf20Sopenharmony_ci	spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
9828c2ecf20Sopenharmony_ci	spk_y = vc->vc_rows - 1;
9838c2ecf20Sopenharmony_ci	say_line(vc);
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic void left_edge(struct vc_data *vc)
9878c2ecf20Sopenharmony_ci{
9888c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
9898c2ecf20Sopenharmony_ci	spk_pos -= spk_x * 2;
9908c2ecf20Sopenharmony_ci	spk_x = 0;
9918c2ecf20Sopenharmony_ci	say_char(vc);
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic void right_edge(struct vc_data *vc)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
9978c2ecf20Sopenharmony_ci	spk_pos += (vc->vc_cols - spk_x - 1) * 2;
9988c2ecf20Sopenharmony_ci	spk_x = vc->vc_cols - 1;
9998c2ecf20Sopenharmony_ci	say_char(vc);
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic void say_first_char(struct vc_data *vc)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	int i, len = get_line(vc);
10058c2ecf20Sopenharmony_ci	u16 ch;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
10088c2ecf20Sopenharmony_ci	if (len == 0) {
10098c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_BLANK));
10108c2ecf20Sopenharmony_ci		return;
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
10138c2ecf20Sopenharmony_ci		if (buf[i] != SPACE)
10148c2ecf20Sopenharmony_ci			break;
10158c2ecf20Sopenharmony_ci	ch = buf[i];
10168c2ecf20Sopenharmony_ci	spk_pos -= (spk_x - i) * 2;
10178c2ecf20Sopenharmony_ci	spk_x = i;
10188c2ecf20Sopenharmony_ci	synth_printf("%d, ", ++i);
10198c2ecf20Sopenharmony_ci	speak_char(ch);
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic void say_last_char(struct vc_data *vc)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	int len = get_line(vc);
10258c2ecf20Sopenharmony_ci	u16 ch;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
10288c2ecf20Sopenharmony_ci	if (len == 0) {
10298c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_BLANK));
10308c2ecf20Sopenharmony_ci		return;
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci	ch = buf[--len];
10338c2ecf20Sopenharmony_ci	spk_pos -= (spk_x - len) * 2;
10348c2ecf20Sopenharmony_ci	spk_x = len;
10358c2ecf20Sopenharmony_ci	synth_printf("%d, ", ++len);
10368c2ecf20Sopenharmony_ci	speak_char(ch);
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_cistatic void say_position(struct vc_data *vc)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
10428c2ecf20Sopenharmony_ci		     vc->vc_num + 1);
10438c2ecf20Sopenharmony_ci	synth_printf("\n");
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci/* Added by brianb */
10478c2ecf20Sopenharmony_cistatic void say_char_num(struct vc_data *vc)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	u_char tmp;
10508c2ecf20Sopenharmony_ci	u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci/* these are stub functions to keep keyboard.c happy. */
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_cistatic void say_from_top(struct vc_data *vc)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	say_screen_from_to(vc, 0, spk_y);
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic void say_to_bottom(struct vc_data *vc)
10638c2ecf20Sopenharmony_ci{
10648c2ecf20Sopenharmony_ci	say_screen_from_to(vc, spk_y, vc->vc_rows);
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic void say_from_left(struct vc_data *vc)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	say_line_from_to(vc, 0, spk_x, 1);
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cistatic void say_to_right(struct vc_data *vc)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	say_line_from_to(vc, spk_x, vc->vc_cols, 1);
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci/* end of stub functions. */
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic void spkup_write(const u16 *in_buf, int count)
10808c2ecf20Sopenharmony_ci{
10818c2ecf20Sopenharmony_ci	static int rep_count;
10828c2ecf20Sopenharmony_ci	static u16 ch = '\0', old_ch = '\0';
10838c2ecf20Sopenharmony_ci	static u_short char_type, last_type;
10848c2ecf20Sopenharmony_ci	int in_count = count;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	spk_keydown = 0;
10878c2ecf20Sopenharmony_ci	while (count--) {
10888c2ecf20Sopenharmony_ci		if (cursor_track == read_all_mode) {
10898c2ecf20Sopenharmony_ci			/* Insert Sentence Index */
10908c2ecf20Sopenharmony_ci			if ((in_buf == sentmarks[bn][currsentence]) &&
10918c2ecf20Sopenharmony_ci			    (currsentence <= numsentences[bn]))
10928c2ecf20Sopenharmony_ci				synth_insert_next_index(currsentence++);
10938c2ecf20Sopenharmony_ci		}
10948c2ecf20Sopenharmony_ci		ch = *in_buf++;
10958c2ecf20Sopenharmony_ci		if (ch < 0x100)
10968c2ecf20Sopenharmony_ci			char_type = spk_chartab[ch];
10978c2ecf20Sopenharmony_ci		else
10988c2ecf20Sopenharmony_ci			char_type = ALPHA;
10998c2ecf20Sopenharmony_ci		if (ch == old_ch && !(char_type & B_NUM)) {
11008c2ecf20Sopenharmony_ci			if (++rep_count > 2)
11018c2ecf20Sopenharmony_ci				continue;
11028c2ecf20Sopenharmony_ci		} else {
11038c2ecf20Sopenharmony_ci			if ((last_type & CH_RPT) && rep_count > 2) {
11048c2ecf20Sopenharmony_ci				synth_printf(" ");
11058c2ecf20Sopenharmony_ci				synth_printf(spk_msg_get(MSG_REPEAT_DESC),
11068c2ecf20Sopenharmony_ci					     ++rep_count);
11078c2ecf20Sopenharmony_ci				synth_printf(" ");
11088c2ecf20Sopenharmony_ci			}
11098c2ecf20Sopenharmony_ci			rep_count = 0;
11108c2ecf20Sopenharmony_ci		}
11118c2ecf20Sopenharmony_ci		if (ch == spk_lastkey) {
11128c2ecf20Sopenharmony_ci			rep_count = 0;
11138c2ecf20Sopenharmony_ci			if (spk_key_echo == 1 && ch >= MINECHOCHAR)
11148c2ecf20Sopenharmony_ci				speak_char(ch);
11158c2ecf20Sopenharmony_ci		} else if (char_type & B_ALPHA) {
11168c2ecf20Sopenharmony_ci			if ((synth_flags & SF_DEC) && (last_type & PUNC))
11178c2ecf20Sopenharmony_ci				synth_buffer_add(SPACE);
11188c2ecf20Sopenharmony_ci			synth_putwc_s(ch);
11198c2ecf20Sopenharmony_ci		} else if (char_type & B_NUM) {
11208c2ecf20Sopenharmony_ci			rep_count = 0;
11218c2ecf20Sopenharmony_ci			synth_putwc_s(ch);
11228c2ecf20Sopenharmony_ci		} else if (char_type & spk_punc_mask) {
11238c2ecf20Sopenharmony_ci			speak_char(ch);
11248c2ecf20Sopenharmony_ci			char_type &= ~PUNC;	/* for dec nospell processing */
11258c2ecf20Sopenharmony_ci		} else if (char_type & SYNTH_OK) {
11268c2ecf20Sopenharmony_ci			/* these are usually puncts like . and , which synth
11278c2ecf20Sopenharmony_ci			 * needs for expression.
11288c2ecf20Sopenharmony_ci			 * suppress multiple to get rid of long pauses and
11298c2ecf20Sopenharmony_ci			 * clear repeat count
11308c2ecf20Sopenharmony_ci			 * so if someone has
11318c2ecf20Sopenharmony_ci			 * repeats on you don't get nothing repeated count
11328c2ecf20Sopenharmony_ci			 */
11338c2ecf20Sopenharmony_ci			if (ch != old_ch)
11348c2ecf20Sopenharmony_ci				synth_putwc_s(ch);
11358c2ecf20Sopenharmony_ci			else
11368c2ecf20Sopenharmony_ci				rep_count = 0;
11378c2ecf20Sopenharmony_ci		} else {
11388c2ecf20Sopenharmony_ci/* send space and record position, if next is num overwrite space */
11398c2ecf20Sopenharmony_ci			if (old_ch != ch)
11408c2ecf20Sopenharmony_ci				synth_buffer_add(SPACE);
11418c2ecf20Sopenharmony_ci			else
11428c2ecf20Sopenharmony_ci				rep_count = 0;
11438c2ecf20Sopenharmony_ci		}
11448c2ecf20Sopenharmony_ci		old_ch = ch;
11458c2ecf20Sopenharmony_ci		last_type = char_type;
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci	spk_lastkey = 0;
11488c2ecf20Sopenharmony_ci	if (in_count > 2 && rep_count > 2) {
11498c2ecf20Sopenharmony_ci		if (last_type & CH_RPT) {
11508c2ecf20Sopenharmony_ci			synth_printf(" ");
11518c2ecf20Sopenharmony_ci			synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
11528c2ecf20Sopenharmony_ci				     ++rep_count);
11538c2ecf20Sopenharmony_ci			synth_printf(" ");
11548c2ecf20Sopenharmony_ci		}
11558c2ecf20Sopenharmony_ci		rep_count = 0;
11568c2ecf20Sopenharmony_ci	}
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistatic const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_cistatic void read_all_doc(struct vc_data *vc);
11628c2ecf20Sopenharmony_cistatic void cursor_done(struct timer_list *unused);
11638c2ecf20Sopenharmony_cistatic DEFINE_TIMER(cursor_timer, cursor_done);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cistatic void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	unsigned long flags;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	if (!synth || up_flag || spk_killed)
11708c2ecf20Sopenharmony_ci		return;
11718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
11728c2ecf20Sopenharmony_ci	if (cursor_track == read_all_mode) {
11738c2ecf20Sopenharmony_ci		switch (value) {
11748c2ecf20Sopenharmony_ci		case KVAL(K_SHIFT):
11758c2ecf20Sopenharmony_ci			del_timer(&cursor_timer);
11768c2ecf20Sopenharmony_ci			spk_shut_up &= 0xfe;
11778c2ecf20Sopenharmony_ci			spk_do_flush();
11788c2ecf20Sopenharmony_ci			read_all_doc(vc);
11798c2ecf20Sopenharmony_ci			break;
11808c2ecf20Sopenharmony_ci		case KVAL(K_CTRL):
11818c2ecf20Sopenharmony_ci			del_timer(&cursor_timer);
11828c2ecf20Sopenharmony_ci			cursor_track = prev_cursor_track;
11838c2ecf20Sopenharmony_ci			spk_shut_up &= 0xfe;
11848c2ecf20Sopenharmony_ci			spk_do_flush();
11858c2ecf20Sopenharmony_ci			break;
11868c2ecf20Sopenharmony_ci		}
11878c2ecf20Sopenharmony_ci	} else {
11888c2ecf20Sopenharmony_ci		spk_shut_up &= 0xfe;
11898c2ecf20Sopenharmony_ci		spk_do_flush();
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci	if (spk_say_ctrl && value < NUM_CTL_LABELS)
11928c2ecf20Sopenharmony_ci		synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
11938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_cistatic void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	unsigned long flags;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
12018c2ecf20Sopenharmony_ci	if (up_flag) {
12028c2ecf20Sopenharmony_ci		spk_lastkey = 0;
12038c2ecf20Sopenharmony_ci		spk_keydown = 0;
12048c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
12058c2ecf20Sopenharmony_ci		return;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci	if (!synth || spk_killed) {
12088c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
12098c2ecf20Sopenharmony_ci		return;
12108c2ecf20Sopenharmony_ci	}
12118c2ecf20Sopenharmony_ci	spk_shut_up &= 0xfe;
12128c2ecf20Sopenharmony_ci	spk_lastkey = value;
12138c2ecf20Sopenharmony_ci	spk_keydown++;
12148c2ecf20Sopenharmony_ci	spk_parked &= 0xfe;
12158c2ecf20Sopenharmony_ci	if (spk_key_echo == 2 && value >= MINECHOCHAR)
12168c2ecf20Sopenharmony_ci		speak_char(value);
12178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
12188c2ecf20Sopenharmony_ci}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ciint spk_set_key_info(const u_char *key_info, u_char *k_buffer)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	int i = 0, states, key_data_len;
12238c2ecf20Sopenharmony_ci	const u_char *cp = key_info;
12248c2ecf20Sopenharmony_ci	u_char *cp1 = k_buffer;
12258c2ecf20Sopenharmony_ci	u_char ch, version, num_keys;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	version = *cp++;
12288c2ecf20Sopenharmony_ci	if (version != KEY_MAP_VER) {
12298c2ecf20Sopenharmony_ci		pr_debug("version found %d should be %d\n",
12308c2ecf20Sopenharmony_ci			 version, KEY_MAP_VER);
12318c2ecf20Sopenharmony_ci		return -EINVAL;
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci	num_keys = *cp;
12348c2ecf20Sopenharmony_ci	states = (int)cp[1];
12358c2ecf20Sopenharmony_ci	key_data_len = (states + 1) * (num_keys + 1);
12368c2ecf20Sopenharmony_ci	if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
12378c2ecf20Sopenharmony_ci		pr_debug("too many key_infos (%d over %u)\n",
12388c2ecf20Sopenharmony_ci			 key_data_len + SHIFT_TBL_SIZE + 4,
12398c2ecf20Sopenharmony_ci			 (unsigned int)(sizeof(spk_key_buf)));
12408c2ecf20Sopenharmony_ci		return -EINVAL;
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci	memset(k_buffer, 0, SHIFT_TBL_SIZE);
12438c2ecf20Sopenharmony_ci	memset(spk_our_keys, 0, sizeof(spk_our_keys));
12448c2ecf20Sopenharmony_ci	spk_shift_table = k_buffer;
12458c2ecf20Sopenharmony_ci	spk_our_keys[0] = spk_shift_table;
12468c2ecf20Sopenharmony_ci	cp1 += SHIFT_TBL_SIZE;
12478c2ecf20Sopenharmony_ci	memcpy(cp1, cp, key_data_len + 3);
12488c2ecf20Sopenharmony_ci	/* get num_keys, states and data */
12498c2ecf20Sopenharmony_ci	cp1 += 2;		/* now pointing at shift states */
12508c2ecf20Sopenharmony_ci	for (i = 1; i <= states; i++) {
12518c2ecf20Sopenharmony_ci		ch = *cp1++;
12528c2ecf20Sopenharmony_ci		if (ch >= SHIFT_TBL_SIZE) {
12538c2ecf20Sopenharmony_ci			pr_debug("(%d) not valid shift state (max_allowed = %d)\n",
12548c2ecf20Sopenharmony_ci				 ch, SHIFT_TBL_SIZE);
12558c2ecf20Sopenharmony_ci			return -EINVAL;
12568c2ecf20Sopenharmony_ci		}
12578c2ecf20Sopenharmony_ci		spk_shift_table[ch] = i;
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci	keymap_flags = *cp1++;
12608c2ecf20Sopenharmony_ci	while ((ch = *cp1)) {
12618c2ecf20Sopenharmony_ci		if (ch >= MAX_KEY) {
12628c2ecf20Sopenharmony_ci			pr_debug("(%d), not valid key, (max_allowed = %d)\n",
12638c2ecf20Sopenharmony_ci				 ch, MAX_KEY);
12648c2ecf20Sopenharmony_ci			return -EINVAL;
12658c2ecf20Sopenharmony_ci		}
12668c2ecf20Sopenharmony_ci		spk_our_keys[ch] = cp1;
12678c2ecf20Sopenharmony_ci		cp1 += states + 1;
12688c2ecf20Sopenharmony_ci	}
12698c2ecf20Sopenharmony_ci	return 0;
12708c2ecf20Sopenharmony_ci}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_cistatic struct var_t spk_vars[] = {
12738c2ecf20Sopenharmony_ci	/* bell must be first to set high limit */
12748c2ecf20Sopenharmony_ci	{BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
12758c2ecf20Sopenharmony_ci	{SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
12768c2ecf20Sopenharmony_ci	{ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
12778c2ecf20Sopenharmony_ci	{BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
12788c2ecf20Sopenharmony_ci	{BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
12798c2ecf20Sopenharmony_ci	{PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
12808c2ecf20Sopenharmony_ci	{READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
12818c2ecf20Sopenharmony_ci	{CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
12828c2ecf20Sopenharmony_ci	{SAY_CONTROL, TOGGLE_0},
12838c2ecf20Sopenharmony_ci	{SAY_WORD_CTL, TOGGLE_0},
12848c2ecf20Sopenharmony_ci	{NO_INTERRUPT, TOGGLE_0},
12858c2ecf20Sopenharmony_ci	{KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
12868c2ecf20Sopenharmony_ci	V_LAST_VAR
12878c2ecf20Sopenharmony_ci};
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_cistatic void toggle_cursoring(struct vc_data *vc)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	if (cursor_track == read_all_mode)
12928c2ecf20Sopenharmony_ci		cursor_track = prev_cursor_track;
12938c2ecf20Sopenharmony_ci	if (++cursor_track >= CT_Max)
12948c2ecf20Sopenharmony_ci		cursor_track = 0;
12958c2ecf20Sopenharmony_ci	synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_civoid spk_reset_default_chars(void)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	int i;
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	/* First, free any non-default */
13038c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++) {
13048c2ecf20Sopenharmony_ci		if (spk_characters[i] &&
13058c2ecf20Sopenharmony_ci		    (spk_characters[i] != spk_default_chars[i]))
13068c2ecf20Sopenharmony_ci			kfree(spk_characters[i]);
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_civoid spk_reset_default_chartab(void)
13138c2ecf20Sopenharmony_ci{
13148c2ecf20Sopenharmony_ci	memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
13158c2ecf20Sopenharmony_ci}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_cistatic const struct st_bits_data *pb_edit;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_cistatic int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
13208c2ecf20Sopenharmony_ci{
13218c2ecf20Sopenharmony_ci	short mask = pb_edit->mask, ch_type = spk_chartab[ch];
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
13248c2ecf20Sopenharmony_ci		return -1;
13258c2ecf20Sopenharmony_ci	if (ch == SPACE) {
13268c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
13278c2ecf20Sopenharmony_ci		spk_special_handler = NULL;
13288c2ecf20Sopenharmony_ci		return 1;
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci	if (mask < PUNC && !(ch_type & PUNC))
13318c2ecf20Sopenharmony_ci		return -1;
13328c2ecf20Sopenharmony_ci	spk_chartab[ch] ^= mask;
13338c2ecf20Sopenharmony_ci	speak_char(ch);
13348c2ecf20Sopenharmony_ci	synth_printf(" %s\n",
13358c2ecf20Sopenharmony_ci		     (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
13368c2ecf20Sopenharmony_ci		     spk_msg_get(MSG_OFF));
13378c2ecf20Sopenharmony_ci	return 1;
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci/* Allocation concurrency is protected by the console semaphore */
13418c2ecf20Sopenharmony_cistatic int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)
13428c2ecf20Sopenharmony_ci{
13438c2ecf20Sopenharmony_ci	int vc_num;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	vc_num = vc->vc_num;
13468c2ecf20Sopenharmony_ci	if (!speakup_console[vc_num]) {
13478c2ecf20Sopenharmony_ci		speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
13488c2ecf20Sopenharmony_ci						  gfp_flags);
13498c2ecf20Sopenharmony_ci		if (!speakup_console[vc_num])
13508c2ecf20Sopenharmony_ci			return -ENOMEM;
13518c2ecf20Sopenharmony_ci		speakup_date(vc);
13528c2ecf20Sopenharmony_ci	} else if (!spk_parked) {
13538c2ecf20Sopenharmony_ci		speakup_date(vc);
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	return 0;
13578c2ecf20Sopenharmony_ci}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_cistatic void speakup_deallocate(struct vc_data *vc)
13608c2ecf20Sopenharmony_ci{
13618c2ecf20Sopenharmony_ci	int vc_num;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	vc_num = vc->vc_num;
13648c2ecf20Sopenharmony_ci	kfree(speakup_console[vc_num]);
13658c2ecf20Sopenharmony_ci	speakup_console[vc_num] = NULL;
13668c2ecf20Sopenharmony_ci}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_cistatic u_char is_cursor;
13698c2ecf20Sopenharmony_cistatic u_long old_cursor_pos, old_cursor_x, old_cursor_y;
13708c2ecf20Sopenharmony_cistatic int cursor_con;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_cistatic void reset_highlight_buffers(struct vc_data *);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic int read_all_key;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_cistatic int in_keyboard_notifier;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_cistatic void start_read_all_timer(struct vc_data *vc, int command);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cienum {
13818c2ecf20Sopenharmony_ci	RA_NOTHING,
13828c2ecf20Sopenharmony_ci	RA_NEXT_SENT,
13838c2ecf20Sopenharmony_ci	RA_PREV_LINE,
13848c2ecf20Sopenharmony_ci	RA_NEXT_LINE,
13858c2ecf20Sopenharmony_ci	RA_PREV_SENT,
13868c2ecf20Sopenharmony_ci	RA_DOWN_ARROW,
13878c2ecf20Sopenharmony_ci	RA_TIMER,
13888c2ecf20Sopenharmony_ci	RA_FIND_NEXT_SENT,
13898c2ecf20Sopenharmony_ci	RA_FIND_PREV_SENT,
13908c2ecf20Sopenharmony_ci};
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_cistatic void kbd_fakekey2(struct vc_data *vc, int command)
13938c2ecf20Sopenharmony_ci{
13948c2ecf20Sopenharmony_ci	del_timer(&cursor_timer);
13958c2ecf20Sopenharmony_ci	speakup_fake_down_arrow();
13968c2ecf20Sopenharmony_ci	start_read_all_timer(vc, command);
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_cistatic void read_all_doc(struct vc_data *vc)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
14028c2ecf20Sopenharmony_ci		return;
14038c2ecf20Sopenharmony_ci	if (!synth_supports_indexing())
14048c2ecf20Sopenharmony_ci		return;
14058c2ecf20Sopenharmony_ci	if (cursor_track != read_all_mode)
14068c2ecf20Sopenharmony_ci		prev_cursor_track = cursor_track;
14078c2ecf20Sopenharmony_ci	cursor_track = read_all_mode;
14088c2ecf20Sopenharmony_ci	spk_reset_index_count(0);
14098c2ecf20Sopenharmony_ci	if (get_sentence_buf(vc, 0) == -1) {
14108c2ecf20Sopenharmony_ci		del_timer(&cursor_timer);
14118c2ecf20Sopenharmony_ci		if (!in_keyboard_notifier)
14128c2ecf20Sopenharmony_ci			speakup_fake_down_arrow();
14138c2ecf20Sopenharmony_ci		start_read_all_timer(vc, RA_DOWN_ARROW);
14148c2ecf20Sopenharmony_ci	} else {
14158c2ecf20Sopenharmony_ci		say_sentence_num(0, 0);
14168c2ecf20Sopenharmony_ci		synth_insert_next_index(0);
14178c2ecf20Sopenharmony_ci		start_read_all_timer(vc, RA_TIMER);
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci}
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_cistatic void stop_read_all(struct vc_data *vc)
14228c2ecf20Sopenharmony_ci{
14238c2ecf20Sopenharmony_ci	del_timer(&cursor_timer);
14248c2ecf20Sopenharmony_ci	cursor_track = prev_cursor_track;
14258c2ecf20Sopenharmony_ci	spk_shut_up &= 0xfe;
14268c2ecf20Sopenharmony_ci	spk_do_flush();
14278c2ecf20Sopenharmony_ci}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic void start_read_all_timer(struct vc_data *vc, int command)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	struct var_t *cursor_timeout;
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	cursor_con = vc->vc_num;
14348c2ecf20Sopenharmony_ci	read_all_key = command;
14358c2ecf20Sopenharmony_ci	cursor_timeout = spk_get_var(CURSOR_TIME);
14368c2ecf20Sopenharmony_ci	mod_timer(&cursor_timer,
14378c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
14388c2ecf20Sopenharmony_ci}
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_cistatic void handle_cursor_read_all(struct vc_data *vc, int command)
14418c2ecf20Sopenharmony_ci{
14428c2ecf20Sopenharmony_ci	int indcount, sentcount, rv, sn;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	switch (command) {
14458c2ecf20Sopenharmony_ci	case RA_NEXT_SENT:
14468c2ecf20Sopenharmony_ci		/* Get Current Sentence */
14478c2ecf20Sopenharmony_ci		spk_get_index_count(&indcount, &sentcount);
14488c2ecf20Sopenharmony_ci		/*printk("%d %d  ", indcount, sentcount); */
14498c2ecf20Sopenharmony_ci		spk_reset_index_count(sentcount + 1);
14508c2ecf20Sopenharmony_ci		if (indcount == 1) {
14518c2ecf20Sopenharmony_ci			if (!say_sentence_num(sentcount + 1, 0)) {
14528c2ecf20Sopenharmony_ci				kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
14538c2ecf20Sopenharmony_ci				return;
14548c2ecf20Sopenharmony_ci			}
14558c2ecf20Sopenharmony_ci			synth_insert_next_index(0);
14568c2ecf20Sopenharmony_ci		} else {
14578c2ecf20Sopenharmony_ci			sn = 0;
14588c2ecf20Sopenharmony_ci			if (!say_sentence_num(sentcount + 1, 1)) {
14598c2ecf20Sopenharmony_ci				sn = 1;
14608c2ecf20Sopenharmony_ci				spk_reset_index_count(sn);
14618c2ecf20Sopenharmony_ci			} else {
14628c2ecf20Sopenharmony_ci				synth_insert_next_index(0);
14638c2ecf20Sopenharmony_ci			}
14648c2ecf20Sopenharmony_ci			if (!say_sentence_num(sn, 0)) {
14658c2ecf20Sopenharmony_ci				kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
14668c2ecf20Sopenharmony_ci				return;
14678c2ecf20Sopenharmony_ci			}
14688c2ecf20Sopenharmony_ci			synth_insert_next_index(0);
14698c2ecf20Sopenharmony_ci		}
14708c2ecf20Sopenharmony_ci		start_read_all_timer(vc, RA_TIMER);
14718c2ecf20Sopenharmony_ci		break;
14728c2ecf20Sopenharmony_ci	case RA_PREV_SENT:
14738c2ecf20Sopenharmony_ci		break;
14748c2ecf20Sopenharmony_ci	case RA_NEXT_LINE:
14758c2ecf20Sopenharmony_ci		read_all_doc(vc);
14768c2ecf20Sopenharmony_ci		break;
14778c2ecf20Sopenharmony_ci	case RA_PREV_LINE:
14788c2ecf20Sopenharmony_ci		break;
14798c2ecf20Sopenharmony_ci	case RA_DOWN_ARROW:
14808c2ecf20Sopenharmony_ci		if (get_sentence_buf(vc, 0) == -1) {
14818c2ecf20Sopenharmony_ci			kbd_fakekey2(vc, RA_DOWN_ARROW);
14828c2ecf20Sopenharmony_ci		} else {
14838c2ecf20Sopenharmony_ci			say_sentence_num(0, 0);
14848c2ecf20Sopenharmony_ci			synth_insert_next_index(0);
14858c2ecf20Sopenharmony_ci			start_read_all_timer(vc, RA_TIMER);
14868c2ecf20Sopenharmony_ci		}
14878c2ecf20Sopenharmony_ci		break;
14888c2ecf20Sopenharmony_ci	case RA_FIND_NEXT_SENT:
14898c2ecf20Sopenharmony_ci		rv = get_sentence_buf(vc, 0);
14908c2ecf20Sopenharmony_ci		if (rv == -1)
14918c2ecf20Sopenharmony_ci			read_all_doc(vc);
14928c2ecf20Sopenharmony_ci		if (rv == 0) {
14938c2ecf20Sopenharmony_ci			kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
14948c2ecf20Sopenharmony_ci		} else {
14958c2ecf20Sopenharmony_ci			say_sentence_num(1, 0);
14968c2ecf20Sopenharmony_ci			synth_insert_next_index(0);
14978c2ecf20Sopenharmony_ci			start_read_all_timer(vc, RA_TIMER);
14988c2ecf20Sopenharmony_ci		}
14998c2ecf20Sopenharmony_ci		break;
15008c2ecf20Sopenharmony_ci	case RA_FIND_PREV_SENT:
15018c2ecf20Sopenharmony_ci		break;
15028c2ecf20Sopenharmony_ci	case RA_TIMER:
15038c2ecf20Sopenharmony_ci		spk_get_index_count(&indcount, &sentcount);
15048c2ecf20Sopenharmony_ci		if (indcount < 2)
15058c2ecf20Sopenharmony_ci			kbd_fakekey2(vc, RA_DOWN_ARROW);
15068c2ecf20Sopenharmony_ci		else
15078c2ecf20Sopenharmony_ci			start_read_all_timer(vc, RA_TIMER);
15088c2ecf20Sopenharmony_ci		break;
15098c2ecf20Sopenharmony_ci	}
15108c2ecf20Sopenharmony_ci}
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_cistatic int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
15138c2ecf20Sopenharmony_ci{
15148c2ecf20Sopenharmony_ci	unsigned long flags;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
15178c2ecf20Sopenharmony_ci	if (cursor_track == read_all_mode) {
15188c2ecf20Sopenharmony_ci		spk_parked &= 0xfe;
15198c2ecf20Sopenharmony_ci		if (!synth || up_flag || spk_shut_up) {
15208c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
15218c2ecf20Sopenharmony_ci			return NOTIFY_STOP;
15228c2ecf20Sopenharmony_ci		}
15238c2ecf20Sopenharmony_ci		del_timer(&cursor_timer);
15248c2ecf20Sopenharmony_ci		spk_shut_up &= 0xfe;
15258c2ecf20Sopenharmony_ci		spk_do_flush();
15268c2ecf20Sopenharmony_ci		start_read_all_timer(vc, value + 1);
15278c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
15288c2ecf20Sopenharmony_ci		return NOTIFY_STOP;
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
15318c2ecf20Sopenharmony_ci	return NOTIFY_OK;
15328c2ecf20Sopenharmony_ci}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_cistatic void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
15358c2ecf20Sopenharmony_ci{
15368c2ecf20Sopenharmony_ci	unsigned long flags;
15378c2ecf20Sopenharmony_ci	struct var_t *cursor_timeout;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
15408c2ecf20Sopenharmony_ci	spk_parked &= 0xfe;
15418c2ecf20Sopenharmony_ci	if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
15428c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
15438c2ecf20Sopenharmony_ci		return;
15448c2ecf20Sopenharmony_ci	}
15458c2ecf20Sopenharmony_ci	spk_shut_up &= 0xfe;
15468c2ecf20Sopenharmony_ci	if (spk_no_intr)
15478c2ecf20Sopenharmony_ci		spk_do_flush();
15488c2ecf20Sopenharmony_ci/* the key press flushes if !no_inter but we want to flush on cursor
15498c2ecf20Sopenharmony_ci * moves regardless of no_inter state
15508c2ecf20Sopenharmony_ci */
15518c2ecf20Sopenharmony_ci	is_cursor = value + 1;
15528c2ecf20Sopenharmony_ci	old_cursor_pos = vc->vc_pos;
15538c2ecf20Sopenharmony_ci	old_cursor_x = vc->state.x;
15548c2ecf20Sopenharmony_ci	old_cursor_y = vc->state.y;
15558c2ecf20Sopenharmony_ci	speakup_console[vc->vc_num]->ht.cy = vc->state.y;
15568c2ecf20Sopenharmony_ci	cursor_con = vc->vc_num;
15578c2ecf20Sopenharmony_ci	if (cursor_track == CT_Highlight)
15588c2ecf20Sopenharmony_ci		reset_highlight_buffers(vc);
15598c2ecf20Sopenharmony_ci	cursor_timeout = spk_get_var(CURSOR_TIME);
15608c2ecf20Sopenharmony_ci	mod_timer(&cursor_timer,
15618c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
15628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
15638c2ecf20Sopenharmony_ci}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_cistatic void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
15668c2ecf20Sopenharmony_ci{
15678c2ecf20Sopenharmony_ci	int i, bi, hi;
15688c2ecf20Sopenharmony_ci	int vc_num = vc->vc_num;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	bi = (vc->vc_attr & 0x70) >> 4;
15718c2ecf20Sopenharmony_ci	hi = speakup_console[vc_num]->ht.highsize[bi];
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	i = 0;
15748c2ecf20Sopenharmony_ci	if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
15758c2ecf20Sopenharmony_ci		speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
15768c2ecf20Sopenharmony_ci		speakup_console[vc_num]->ht.rx[bi] = vc->state.x;
15778c2ecf20Sopenharmony_ci		speakup_console[vc_num]->ht.ry[bi] = vc->state.y;
15788c2ecf20Sopenharmony_ci	}
15798c2ecf20Sopenharmony_ci	while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
15808c2ecf20Sopenharmony_ci		if (ic[i] > 32) {
15818c2ecf20Sopenharmony_ci			speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
15828c2ecf20Sopenharmony_ci			hi++;
15838c2ecf20Sopenharmony_ci		} else if ((ic[i] == 32) && (hi != 0)) {
15848c2ecf20Sopenharmony_ci			if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
15858c2ecf20Sopenharmony_ci			    32) {
15868c2ecf20Sopenharmony_ci				speakup_console[vc_num]->ht.highbuf[bi][hi] =
15878c2ecf20Sopenharmony_ci				    ic[i];
15888c2ecf20Sopenharmony_ci				hi++;
15898c2ecf20Sopenharmony_ci			}
15908c2ecf20Sopenharmony_ci		}
15918c2ecf20Sopenharmony_ci		i++;
15928c2ecf20Sopenharmony_ci	}
15938c2ecf20Sopenharmony_ci	speakup_console[vc_num]->ht.highsize[bi] = hi;
15948c2ecf20Sopenharmony_ci}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_cistatic void reset_highlight_buffers(struct vc_data *vc)
15978c2ecf20Sopenharmony_ci{
15988c2ecf20Sopenharmony_ci	int i;
15998c2ecf20Sopenharmony_ci	int vc_num = vc->vc_num;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
16028c2ecf20Sopenharmony_ci		speakup_console[vc_num]->ht.highsize[i] = 0;
16038c2ecf20Sopenharmony_ci}
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_cistatic int count_highlight_color(struct vc_data *vc)
16068c2ecf20Sopenharmony_ci{
16078c2ecf20Sopenharmony_ci	int i, bg;
16088c2ecf20Sopenharmony_ci	int cc;
16098c2ecf20Sopenharmony_ci	int vc_num = vc->vc_num;
16108c2ecf20Sopenharmony_ci	u16 ch;
16118c2ecf20Sopenharmony_ci	u16 *start = (u16 *)vc->vc_origin;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
16148c2ecf20Sopenharmony_ci		speakup_console[vc_num]->ht.bgcount[i] = 0;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	for (i = 0; i < vc->vc_rows; i++) {
16178c2ecf20Sopenharmony_ci		u16 *end = start + vc->vc_cols * 2;
16188c2ecf20Sopenharmony_ci		u16 *ptr;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci		for (ptr = start; ptr < end; ptr++) {
16218c2ecf20Sopenharmony_ci			ch = get_attributes(vc, ptr);
16228c2ecf20Sopenharmony_ci			bg = (ch & 0x70) >> 4;
16238c2ecf20Sopenharmony_ci			speakup_console[vc_num]->ht.bgcount[bg]++;
16248c2ecf20Sopenharmony_ci		}
16258c2ecf20Sopenharmony_ci		start += vc->vc_size_row;
16268c2ecf20Sopenharmony_ci	}
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	cc = 0;
16298c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
16308c2ecf20Sopenharmony_ci		if (speakup_console[vc_num]->ht.bgcount[i] > 0)
16318c2ecf20Sopenharmony_ci			cc++;
16328c2ecf20Sopenharmony_ci	return cc;
16338c2ecf20Sopenharmony_ci}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_cistatic int get_highlight_color(struct vc_data *vc)
16368c2ecf20Sopenharmony_ci{
16378c2ecf20Sopenharmony_ci	int i, j;
16388c2ecf20Sopenharmony_ci	unsigned int cptr[8];
16398c2ecf20Sopenharmony_ci	int vc_num = vc->vc_num;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
16428c2ecf20Sopenharmony_ci		cptr[i] = i;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	for (i = 0; i < 7; i++)
16458c2ecf20Sopenharmony_ci		for (j = i + 1; j < 8; j++)
16468c2ecf20Sopenharmony_ci			if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
16478c2ecf20Sopenharmony_ci			    speakup_console[vc_num]->ht.bgcount[cptr[j]])
16488c2ecf20Sopenharmony_ci				swap(cptr[i], cptr[j]);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
16518c2ecf20Sopenharmony_ci		if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
16528c2ecf20Sopenharmony_ci			if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
16538c2ecf20Sopenharmony_ci				return cptr[i];
16548c2ecf20Sopenharmony_ci	return -1;
16558c2ecf20Sopenharmony_ci}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_cistatic int speak_highlight(struct vc_data *vc)
16588c2ecf20Sopenharmony_ci{
16598c2ecf20Sopenharmony_ci	int hc, d;
16608c2ecf20Sopenharmony_ci	int vc_num = vc->vc_num;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	if (count_highlight_color(vc) == 1)
16638c2ecf20Sopenharmony_ci		return 0;
16648c2ecf20Sopenharmony_ci	hc = get_highlight_color(vc);
16658c2ecf20Sopenharmony_ci	if (hc != -1) {
16668c2ecf20Sopenharmony_ci		d = vc->state.y - speakup_console[vc_num]->ht.cy;
16678c2ecf20Sopenharmony_ci		if ((d == 1) || (d == -1))
16688c2ecf20Sopenharmony_ci			if (speakup_console[vc_num]->ht.ry[hc] != vc->state.y)
16698c2ecf20Sopenharmony_ci				return 0;
16708c2ecf20Sopenharmony_ci		spk_parked |= 0x01;
16718c2ecf20Sopenharmony_ci		spk_do_flush();
16728c2ecf20Sopenharmony_ci		spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
16738c2ecf20Sopenharmony_ci			    speakup_console[vc_num]->ht.highsize[hc]);
16748c2ecf20Sopenharmony_ci		spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
16758c2ecf20Sopenharmony_ci		spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
16768c2ecf20Sopenharmony_ci		spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
16778c2ecf20Sopenharmony_ci		return 1;
16788c2ecf20Sopenharmony_ci	}
16798c2ecf20Sopenharmony_ci	return 0;
16808c2ecf20Sopenharmony_ci}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_cistatic void cursor_done(struct timer_list *unused)
16838c2ecf20Sopenharmony_ci{
16848c2ecf20Sopenharmony_ci	struct vc_data *vc = vc_cons[cursor_con].d;
16858c2ecf20Sopenharmony_ci	unsigned long flags;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	del_timer(&cursor_timer);
16888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
16898c2ecf20Sopenharmony_ci	if (cursor_con != fg_console) {
16908c2ecf20Sopenharmony_ci		is_cursor = 0;
16918c2ecf20Sopenharmony_ci		goto out;
16928c2ecf20Sopenharmony_ci	}
16938c2ecf20Sopenharmony_ci	speakup_date(vc);
16948c2ecf20Sopenharmony_ci	if (win_enabled) {
16958c2ecf20Sopenharmony_ci		if (vc->state.x >= win_left && vc->state.x <= win_right &&
16968c2ecf20Sopenharmony_ci		    vc->state.y >= win_top && vc->state.y <= win_bottom) {
16978c2ecf20Sopenharmony_ci			spk_keydown = 0;
16988c2ecf20Sopenharmony_ci			is_cursor = 0;
16998c2ecf20Sopenharmony_ci			goto out;
17008c2ecf20Sopenharmony_ci		}
17018c2ecf20Sopenharmony_ci	}
17028c2ecf20Sopenharmony_ci	if (cursor_track == read_all_mode) {
17038c2ecf20Sopenharmony_ci		handle_cursor_read_all(vc, read_all_key);
17048c2ecf20Sopenharmony_ci		goto out;
17058c2ecf20Sopenharmony_ci	}
17068c2ecf20Sopenharmony_ci	if (cursor_track == CT_Highlight) {
17078c2ecf20Sopenharmony_ci		if (speak_highlight(vc)) {
17088c2ecf20Sopenharmony_ci			spk_keydown = 0;
17098c2ecf20Sopenharmony_ci			is_cursor = 0;
17108c2ecf20Sopenharmony_ci			goto out;
17118c2ecf20Sopenharmony_ci		}
17128c2ecf20Sopenharmony_ci	}
17138c2ecf20Sopenharmony_ci	if (cursor_track == CT_Window)
17148c2ecf20Sopenharmony_ci		speakup_win_say(vc);
17158c2ecf20Sopenharmony_ci	else if (is_cursor == 1 || is_cursor == 4)
17168c2ecf20Sopenharmony_ci		say_line_from_to(vc, 0, vc->vc_cols, 0);
17178c2ecf20Sopenharmony_ci	else
17188c2ecf20Sopenharmony_ci		say_char(vc);
17198c2ecf20Sopenharmony_ci	spk_keydown = 0;
17208c2ecf20Sopenharmony_ci	is_cursor = 0;
17218c2ecf20Sopenharmony_ciout:
17228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
17238c2ecf20Sopenharmony_ci}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci/* called by: vt_notifier_call() */
17268c2ecf20Sopenharmony_cistatic void speakup_bs(struct vc_data *vc)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	unsigned long flags;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	if (!speakup_console[vc->vc_num])
17318c2ecf20Sopenharmony_ci		return;
17328c2ecf20Sopenharmony_ci	if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
17338c2ecf20Sopenharmony_ci		/* Speakup output, discard */
17348c2ecf20Sopenharmony_ci		return;
17358c2ecf20Sopenharmony_ci	if (!spk_parked)
17368c2ecf20Sopenharmony_ci		speakup_date(vc);
17378c2ecf20Sopenharmony_ci	if (spk_shut_up || !synth) {
17388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
17398c2ecf20Sopenharmony_ci		return;
17408c2ecf20Sopenharmony_ci	}
17418c2ecf20Sopenharmony_ci	if (vc->vc_num == fg_console && spk_keydown) {
17428c2ecf20Sopenharmony_ci		spk_keydown = 0;
17438c2ecf20Sopenharmony_ci		if (!is_cursor)
17448c2ecf20Sopenharmony_ci			say_char(vc);
17458c2ecf20Sopenharmony_ci	}
17468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
17478c2ecf20Sopenharmony_ci}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci/* called by: vt_notifier_call() */
17508c2ecf20Sopenharmony_cistatic void speakup_con_write(struct vc_data *vc, u16 *str, int len)
17518c2ecf20Sopenharmony_ci{
17528c2ecf20Sopenharmony_ci	unsigned long flags;
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
17558c2ecf20Sopenharmony_ci		return;
17568c2ecf20Sopenharmony_ci	if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
17578c2ecf20Sopenharmony_ci		/* Speakup output, discard */
17588c2ecf20Sopenharmony_ci		return;
17598c2ecf20Sopenharmony_ci	if (spk_bell_pos && spk_keydown && (vc->state.x == spk_bell_pos - 1))
17608c2ecf20Sopenharmony_ci		bleep(3);
17618c2ecf20Sopenharmony_ci	if ((is_cursor) || (cursor_track == read_all_mode)) {
17628c2ecf20Sopenharmony_ci		if (cursor_track == CT_Highlight)
17638c2ecf20Sopenharmony_ci			update_color_buffer(vc, str, len);
17648c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
17658c2ecf20Sopenharmony_ci		return;
17668c2ecf20Sopenharmony_ci	}
17678c2ecf20Sopenharmony_ci	if (win_enabled) {
17688c2ecf20Sopenharmony_ci		if (vc->state.x >= win_left && vc->state.x <= win_right &&
17698c2ecf20Sopenharmony_ci		    vc->state.y >= win_top && vc->state.y <= win_bottom) {
17708c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
17718c2ecf20Sopenharmony_ci			return;
17728c2ecf20Sopenharmony_ci		}
17738c2ecf20Sopenharmony_ci	}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	spkup_write(str, len);
17768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
17778c2ecf20Sopenharmony_ci}
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_cistatic void speakup_con_update(struct vc_data *vc)
17808c2ecf20Sopenharmony_ci{
17818c2ecf20Sopenharmony_ci	unsigned long flags;
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	if (!speakup_console[vc->vc_num] || spk_parked || !synth)
17848c2ecf20Sopenharmony_ci		return;
17858c2ecf20Sopenharmony_ci	if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
17868c2ecf20Sopenharmony_ci		/* Speakup output, discard */
17878c2ecf20Sopenharmony_ci		return;
17888c2ecf20Sopenharmony_ci	speakup_date(vc);
17898c2ecf20Sopenharmony_ci	if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) {
17908c2ecf20Sopenharmony_ci		synth_printf("%s", spk_str_pause);
17918c2ecf20Sopenharmony_ci		spk_paused = true;
17928c2ecf20Sopenharmony_ci	}
17938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
17948c2ecf20Sopenharmony_ci}
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_cistatic void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
17978c2ecf20Sopenharmony_ci{
17988c2ecf20Sopenharmony_ci	unsigned long flags;
17998c2ecf20Sopenharmony_ci	int on_off = 2;
18008c2ecf20Sopenharmony_ci	char *label;
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	if (!synth || up_flag || spk_killed)
18038c2ecf20Sopenharmony_ci		return;
18048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
18058c2ecf20Sopenharmony_ci	spk_shut_up &= 0xfe;
18068c2ecf20Sopenharmony_ci	if (spk_no_intr)
18078c2ecf20Sopenharmony_ci		spk_do_flush();
18088c2ecf20Sopenharmony_ci	switch (value) {
18098c2ecf20Sopenharmony_ci	case KVAL(K_CAPS):
18108c2ecf20Sopenharmony_ci		label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
18118c2ecf20Sopenharmony_ci		on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
18128c2ecf20Sopenharmony_ci		break;
18138c2ecf20Sopenharmony_ci	case KVAL(K_NUM):
18148c2ecf20Sopenharmony_ci		label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
18158c2ecf20Sopenharmony_ci		on_off = vt_get_leds(fg_console, VC_NUMLOCK);
18168c2ecf20Sopenharmony_ci		break;
18178c2ecf20Sopenharmony_ci	case KVAL(K_HOLD):
18188c2ecf20Sopenharmony_ci		label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
18198c2ecf20Sopenharmony_ci		on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
18208c2ecf20Sopenharmony_ci		if (speakup_console[vc->vc_num])
18218c2ecf20Sopenharmony_ci			speakup_console[vc->vc_num]->tty_stopped = on_off;
18228c2ecf20Sopenharmony_ci		break;
18238c2ecf20Sopenharmony_ci	default:
18248c2ecf20Sopenharmony_ci		spk_parked &= 0xfe;
18258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
18268c2ecf20Sopenharmony_ci		return;
18278c2ecf20Sopenharmony_ci	}
18288c2ecf20Sopenharmony_ci	if (on_off < 2)
18298c2ecf20Sopenharmony_ci		synth_printf("%s %s\n",
18308c2ecf20Sopenharmony_ci			     label, spk_msg_get(MSG_STATUS_START + on_off));
18318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
18328c2ecf20Sopenharmony_ci}
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_cistatic int inc_dec_var(u_char value)
18358c2ecf20Sopenharmony_ci{
18368c2ecf20Sopenharmony_ci	struct st_var_header *p_header;
18378c2ecf20Sopenharmony_ci	struct var_t *var_data;
18388c2ecf20Sopenharmony_ci	char num_buf[32];
18398c2ecf20Sopenharmony_ci	char *cp = num_buf;
18408c2ecf20Sopenharmony_ci	char *pn;
18418c2ecf20Sopenharmony_ci	int var_id = (int)value - VAR_START;
18428c2ecf20Sopenharmony_ci	int how = (var_id & 1) ? E_INC : E_DEC;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	var_id = var_id / 2 + FIRST_SET_VAR;
18458c2ecf20Sopenharmony_ci	p_header = spk_get_var_header(var_id);
18468c2ecf20Sopenharmony_ci	if (!p_header)
18478c2ecf20Sopenharmony_ci		return -1;
18488c2ecf20Sopenharmony_ci	if (p_header->var_type != VAR_NUM)
18498c2ecf20Sopenharmony_ci		return -1;
18508c2ecf20Sopenharmony_ci	var_data = p_header->data;
18518c2ecf20Sopenharmony_ci	if (spk_set_num_var(1, p_header, how) != 0)
18528c2ecf20Sopenharmony_ci		return -1;
18538c2ecf20Sopenharmony_ci	if (!spk_close_press) {
18548c2ecf20Sopenharmony_ci		for (pn = p_header->name; *pn; pn++) {
18558c2ecf20Sopenharmony_ci			if (*pn == '_')
18568c2ecf20Sopenharmony_ci				*cp = SPACE;
18578c2ecf20Sopenharmony_ci			else
18588c2ecf20Sopenharmony_ci				*cp++ = *pn;
18598c2ecf20Sopenharmony_ci		}
18608c2ecf20Sopenharmony_ci	}
18618c2ecf20Sopenharmony_ci	snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
18628c2ecf20Sopenharmony_ci		 var_data->u.n.value);
18638c2ecf20Sopenharmony_ci	synth_printf("%s", num_buf);
18648c2ecf20Sopenharmony_ci	return 0;
18658c2ecf20Sopenharmony_ci}
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_cistatic void speakup_win_set(struct vc_data *vc)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	char info[40];
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	if (win_start > 1) {
18728c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
18738c2ecf20Sopenharmony_ci		return;
18748c2ecf20Sopenharmony_ci	}
18758c2ecf20Sopenharmony_ci	if (spk_x < win_left || spk_y < win_top) {
18768c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
18778c2ecf20Sopenharmony_ci		return;
18788c2ecf20Sopenharmony_ci	}
18798c2ecf20Sopenharmony_ci	if (win_start && spk_x == win_left && spk_y == win_top) {
18808c2ecf20Sopenharmony_ci		win_left = 0;
18818c2ecf20Sopenharmony_ci		win_right = vc->vc_cols - 1;
18828c2ecf20Sopenharmony_ci		win_bottom = spk_y;
18838c2ecf20Sopenharmony_ci		snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
18848c2ecf20Sopenharmony_ci			 (int)win_top + 1);
18858c2ecf20Sopenharmony_ci	} else {
18868c2ecf20Sopenharmony_ci		if (!win_start) {
18878c2ecf20Sopenharmony_ci			win_top = spk_y;
18888c2ecf20Sopenharmony_ci			win_left = spk_x;
18898c2ecf20Sopenharmony_ci		} else {
18908c2ecf20Sopenharmony_ci			win_bottom = spk_y;
18918c2ecf20Sopenharmony_ci			win_right = spk_x;
18928c2ecf20Sopenharmony_ci		}
18938c2ecf20Sopenharmony_ci		snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
18948c2ecf20Sopenharmony_ci			 (win_start) ?
18958c2ecf20Sopenharmony_ci				spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
18968c2ecf20Sopenharmony_ci			 (int)spk_y + 1, (int)spk_x + 1);
18978c2ecf20Sopenharmony_ci	}
18988c2ecf20Sopenharmony_ci	synth_printf("%s\n", info);
18998c2ecf20Sopenharmony_ci	win_start++;
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_cistatic void speakup_win_clear(struct vc_data *vc)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci	win_top = 0;
19058c2ecf20Sopenharmony_ci	win_bottom = 0;
19068c2ecf20Sopenharmony_ci	win_left = 0;
19078c2ecf20Sopenharmony_ci	win_right = 0;
19088c2ecf20Sopenharmony_ci	win_start = 0;
19098c2ecf20Sopenharmony_ci	synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
19108c2ecf20Sopenharmony_ci}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic void speakup_win_enable(struct vc_data *vc)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	if (win_start < 2) {
19158c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
19168c2ecf20Sopenharmony_ci		return;
19178c2ecf20Sopenharmony_ci	}
19188c2ecf20Sopenharmony_ci	win_enabled ^= 1;
19198c2ecf20Sopenharmony_ci	if (win_enabled)
19208c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
19218c2ecf20Sopenharmony_ci	else
19228c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
19238c2ecf20Sopenharmony_ci}
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_cistatic void speakup_bits(struct vc_data *vc)
19268c2ecf20Sopenharmony_ci{
19278c2ecf20Sopenharmony_ci	int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	if (spk_special_handler || val < 1 || val > 6) {
19308c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_ERROR));
19318c2ecf20Sopenharmony_ci		return;
19328c2ecf20Sopenharmony_ci	}
19338c2ecf20Sopenharmony_ci	pb_edit = &spk_punc_info[val];
19348c2ecf20Sopenharmony_ci	synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
19358c2ecf20Sopenharmony_ci	spk_special_handler = edit_bits;
19368c2ecf20Sopenharmony_ci}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_cistatic int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
19398c2ecf20Sopenharmony_ci{
19408c2ecf20Sopenharmony_ci	static u_char goto_buf[8];
19418c2ecf20Sopenharmony_ci	static int num;
19428c2ecf20Sopenharmony_ci	int maxlen;
19438c2ecf20Sopenharmony_ci	char *cp;
19448c2ecf20Sopenharmony_ci	u16 wch;
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci	if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
19478c2ecf20Sopenharmony_ci		goto do_goto;
19488c2ecf20Sopenharmony_ci	if (type == KT_LATIN && ch == '\n')
19498c2ecf20Sopenharmony_ci		goto do_goto;
19508c2ecf20Sopenharmony_ci	if (type != 0)
19518c2ecf20Sopenharmony_ci		goto oops;
19528c2ecf20Sopenharmony_ci	if (ch == 8) {
19538c2ecf20Sopenharmony_ci		u16 wch;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci		if (num == 0)
19568c2ecf20Sopenharmony_ci			return -1;
19578c2ecf20Sopenharmony_ci		wch = goto_buf[--num];
19588c2ecf20Sopenharmony_ci		goto_buf[num] = '\0';
19598c2ecf20Sopenharmony_ci		spkup_write(&wch, 1);
19608c2ecf20Sopenharmony_ci		return 1;
19618c2ecf20Sopenharmony_ci	}
19628c2ecf20Sopenharmony_ci	if (ch < '+' || ch > 'y')
19638c2ecf20Sopenharmony_ci		goto oops;
19648c2ecf20Sopenharmony_ci	wch = ch;
19658c2ecf20Sopenharmony_ci	goto_buf[num++] = ch;
19668c2ecf20Sopenharmony_ci	goto_buf[num] = '\0';
19678c2ecf20Sopenharmony_ci	spkup_write(&wch, 1);
19688c2ecf20Sopenharmony_ci	maxlen = (*goto_buf >= '0') ? 3 : 4;
19698c2ecf20Sopenharmony_ci	if ((ch == '+' || ch == '-') && num == 1)
19708c2ecf20Sopenharmony_ci		return 1;
19718c2ecf20Sopenharmony_ci	if (ch >= '0' && ch <= '9' && num < maxlen)
19728c2ecf20Sopenharmony_ci		return 1;
19738c2ecf20Sopenharmony_ci	if (num < maxlen - 1 || num > maxlen)
19748c2ecf20Sopenharmony_ci		goto oops;
19758c2ecf20Sopenharmony_ci	if (ch < 'x' || ch > 'y') {
19768c2ecf20Sopenharmony_cioops:
19778c2ecf20Sopenharmony_ci		if (!spk_killed)
19788c2ecf20Sopenharmony_ci			synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
19798c2ecf20Sopenharmony_ci		goto_buf[num = 0] = '\0';
19808c2ecf20Sopenharmony_ci		spk_special_handler = NULL;
19818c2ecf20Sopenharmony_ci		return 1;
19828c2ecf20Sopenharmony_ci	}
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	/* Do not replace with kstrtoul: here we need cp to be updated */
19858c2ecf20Sopenharmony_ci	goto_pos = simple_strtoul(goto_buf, &cp, 10);
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	if (*cp == 'x') {
19888c2ecf20Sopenharmony_ci		if (*goto_buf < '0')
19898c2ecf20Sopenharmony_ci			goto_pos += spk_x;
19908c2ecf20Sopenharmony_ci		else if (goto_pos > 0)
19918c2ecf20Sopenharmony_ci			goto_pos--;
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci		if (goto_pos >= vc->vc_cols)
19948c2ecf20Sopenharmony_ci			goto_pos = vc->vc_cols - 1;
19958c2ecf20Sopenharmony_ci		goto_x = 1;
19968c2ecf20Sopenharmony_ci	} else {
19978c2ecf20Sopenharmony_ci		if (*goto_buf < '0')
19988c2ecf20Sopenharmony_ci			goto_pos += spk_y;
19998c2ecf20Sopenharmony_ci		else if (goto_pos > 0)
20008c2ecf20Sopenharmony_ci			goto_pos--;
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci		if (goto_pos >= vc->vc_rows)
20038c2ecf20Sopenharmony_ci			goto_pos = vc->vc_rows - 1;
20048c2ecf20Sopenharmony_ci		goto_x = 0;
20058c2ecf20Sopenharmony_ci	}
20068c2ecf20Sopenharmony_ci	goto_buf[num = 0] = '\0';
20078c2ecf20Sopenharmony_cido_goto:
20088c2ecf20Sopenharmony_ci	spk_special_handler = NULL;
20098c2ecf20Sopenharmony_ci	spk_parked |= 0x01;
20108c2ecf20Sopenharmony_ci	if (goto_x) {
20118c2ecf20Sopenharmony_ci		spk_pos -= spk_x * 2;
20128c2ecf20Sopenharmony_ci		spk_x = goto_pos;
20138c2ecf20Sopenharmony_ci		spk_pos += goto_pos * 2;
20148c2ecf20Sopenharmony_ci		say_word(vc);
20158c2ecf20Sopenharmony_ci	} else {
20168c2ecf20Sopenharmony_ci		spk_y = goto_pos;
20178c2ecf20Sopenharmony_ci		spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
20188c2ecf20Sopenharmony_ci		say_line(vc);
20198c2ecf20Sopenharmony_ci	}
20208c2ecf20Sopenharmony_ci	return 1;
20218c2ecf20Sopenharmony_ci}
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_cistatic void speakup_goto(struct vc_data *vc)
20248c2ecf20Sopenharmony_ci{
20258c2ecf20Sopenharmony_ci	if (spk_special_handler) {
20268c2ecf20Sopenharmony_ci		synth_printf("%s\n", spk_msg_get(MSG_ERROR));
20278c2ecf20Sopenharmony_ci		return;
20288c2ecf20Sopenharmony_ci	}
20298c2ecf20Sopenharmony_ci	synth_printf("%s\n", spk_msg_get(MSG_GOTO));
20308c2ecf20Sopenharmony_ci	spk_special_handler = handle_goto;
20318c2ecf20Sopenharmony_ci}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_cistatic void speakup_help(struct vc_data *vc)
20348c2ecf20Sopenharmony_ci{
20358c2ecf20Sopenharmony_ci	spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
20368c2ecf20Sopenharmony_ci}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_cistatic void do_nothing(struct vc_data *vc)
20398c2ecf20Sopenharmony_ci{
20408c2ecf20Sopenharmony_ci	return;			/* flush done in do_spkup */
20418c2ecf20Sopenharmony_ci}
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_cistatic u_char key_speakup, spk_key_locked;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_cistatic void speakup_lock(struct vc_data *vc)
20468c2ecf20Sopenharmony_ci{
20478c2ecf20Sopenharmony_ci	if (!spk_key_locked) {
20488c2ecf20Sopenharmony_ci		spk_key_locked = 16;
20498c2ecf20Sopenharmony_ci		key_speakup = 16;
20508c2ecf20Sopenharmony_ci	} else {
20518c2ecf20Sopenharmony_ci		spk_key_locked = 0;
20528c2ecf20Sopenharmony_ci		key_speakup = 0;
20538c2ecf20Sopenharmony_ci	}
20548c2ecf20Sopenharmony_ci}
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_citypedef void (*spkup_hand) (struct vc_data *);
20578c2ecf20Sopenharmony_cistatic spkup_hand spkup_handler[] = {
20588c2ecf20Sopenharmony_ci	/* must be ordered same as defines in speakup.h */
20598c2ecf20Sopenharmony_ci	do_nothing, speakup_goto, speech_kill, speakup_shut_up,
20608c2ecf20Sopenharmony_ci	speakup_cut, speakup_paste, say_first_char, say_last_char,
20618c2ecf20Sopenharmony_ci	say_char, say_prev_char, say_next_char,
20628c2ecf20Sopenharmony_ci	say_word, say_prev_word, say_next_word,
20638c2ecf20Sopenharmony_ci	say_line, say_prev_line, say_next_line,
20648c2ecf20Sopenharmony_ci	top_edge, bottom_edge, left_edge, right_edge,
20658c2ecf20Sopenharmony_ci	spell_word, spell_word, say_screen,
20668c2ecf20Sopenharmony_ci	say_position, say_attributes,
20678c2ecf20Sopenharmony_ci	speakup_off, speakup_parked, say_line,	/* this is for indent */
20688c2ecf20Sopenharmony_ci	say_from_top, say_to_bottom,
20698c2ecf20Sopenharmony_ci	say_from_left, say_to_right,
20708c2ecf20Sopenharmony_ci	say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
20718c2ecf20Sopenharmony_ci	speakup_bits, speakup_bits, speakup_bits,
20728c2ecf20Sopenharmony_ci	speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
20738c2ecf20Sopenharmony_ci	speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
20748c2ecf20Sopenharmony_ci};
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_cistatic void do_spkup(struct vc_data *vc, u_char value)
20778c2ecf20Sopenharmony_ci{
20788c2ecf20Sopenharmony_ci	if (spk_killed && value != SPEECH_KILL)
20798c2ecf20Sopenharmony_ci		return;
20808c2ecf20Sopenharmony_ci	spk_keydown = 0;
20818c2ecf20Sopenharmony_ci	spk_lastkey = 0;
20828c2ecf20Sopenharmony_ci	spk_shut_up &= 0xfe;
20838c2ecf20Sopenharmony_ci	this_speakup_key = value;
20848c2ecf20Sopenharmony_ci	if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
20858c2ecf20Sopenharmony_ci		spk_do_flush();
20868c2ecf20Sopenharmony_ci		(*spkup_handler[value]) (vc);
20878c2ecf20Sopenharmony_ci	} else {
20888c2ecf20Sopenharmony_ci		if (inc_dec_var(value) < 0)
20898c2ecf20Sopenharmony_ci			bleep(9);
20908c2ecf20Sopenharmony_ci	}
20918c2ecf20Sopenharmony_ci}
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_cistatic const char *pad_chars = "0123456789+-*/\015,.?()";
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_cistatic int
20968c2ecf20Sopenharmony_cispeakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
20978c2ecf20Sopenharmony_ci	    int up_flag)
20988c2ecf20Sopenharmony_ci{
20998c2ecf20Sopenharmony_ci	unsigned long flags;
21008c2ecf20Sopenharmony_ci	int kh;
21018c2ecf20Sopenharmony_ci	u_char *key_info;
21028c2ecf20Sopenharmony_ci	u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
21038c2ecf20Sopenharmony_ci	u_char shift_info, offset;
21048c2ecf20Sopenharmony_ci	int ret = 0;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	if (!synth)
21078c2ecf20Sopenharmony_ci		return 0;
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
21108c2ecf20Sopenharmony_ci	tty = vc->port.tty;
21118c2ecf20Sopenharmony_ci	if (type >= 0xf0)
21128c2ecf20Sopenharmony_ci		type -= 0xf0;
21138c2ecf20Sopenharmony_ci	if (type == KT_PAD &&
21148c2ecf20Sopenharmony_ci	    (vt_get_leds(fg_console, VC_NUMLOCK))) {
21158c2ecf20Sopenharmony_ci		if (up_flag) {
21168c2ecf20Sopenharmony_ci			spk_keydown = 0;
21178c2ecf20Sopenharmony_ci			goto out;
21188c2ecf20Sopenharmony_ci		}
21198c2ecf20Sopenharmony_ci		value = pad_chars[value];
21208c2ecf20Sopenharmony_ci		spk_lastkey = value;
21218c2ecf20Sopenharmony_ci		spk_keydown++;
21228c2ecf20Sopenharmony_ci		spk_parked &= 0xfe;
21238c2ecf20Sopenharmony_ci		goto no_map;
21248c2ecf20Sopenharmony_ci	}
21258c2ecf20Sopenharmony_ci	if (keycode >= MAX_KEY)
21268c2ecf20Sopenharmony_ci		goto no_map;
21278c2ecf20Sopenharmony_ci	key_info = spk_our_keys[keycode];
21288c2ecf20Sopenharmony_ci	if (!key_info)
21298c2ecf20Sopenharmony_ci		goto no_map;
21308c2ecf20Sopenharmony_ci	/* Check valid read all mode keys */
21318c2ecf20Sopenharmony_ci	if ((cursor_track == read_all_mode) && (!up_flag)) {
21328c2ecf20Sopenharmony_ci		switch (value) {
21338c2ecf20Sopenharmony_ci		case KVAL(K_DOWN):
21348c2ecf20Sopenharmony_ci		case KVAL(K_UP):
21358c2ecf20Sopenharmony_ci		case KVAL(K_LEFT):
21368c2ecf20Sopenharmony_ci		case KVAL(K_RIGHT):
21378c2ecf20Sopenharmony_ci		case KVAL(K_PGUP):
21388c2ecf20Sopenharmony_ci		case KVAL(K_PGDN):
21398c2ecf20Sopenharmony_ci			break;
21408c2ecf20Sopenharmony_ci		default:
21418c2ecf20Sopenharmony_ci			stop_read_all(vc);
21428c2ecf20Sopenharmony_ci			break;
21438c2ecf20Sopenharmony_ci		}
21448c2ecf20Sopenharmony_ci	}
21458c2ecf20Sopenharmony_ci	shift_info = (shift_state & 0x0f) + key_speakup;
21468c2ecf20Sopenharmony_ci	offset = spk_shift_table[shift_info];
21478c2ecf20Sopenharmony_ci	if (offset) {
21488c2ecf20Sopenharmony_ci		new_key = key_info[offset];
21498c2ecf20Sopenharmony_ci		if (new_key) {
21508c2ecf20Sopenharmony_ci			ret = 1;
21518c2ecf20Sopenharmony_ci			if (new_key == SPK_KEY) {
21528c2ecf20Sopenharmony_ci				if (!spk_key_locked)
21538c2ecf20Sopenharmony_ci					key_speakup = (up_flag) ? 0 : 16;
21548c2ecf20Sopenharmony_ci				if (up_flag || spk_killed)
21558c2ecf20Sopenharmony_ci					goto out;
21568c2ecf20Sopenharmony_ci				spk_shut_up &= 0xfe;
21578c2ecf20Sopenharmony_ci				spk_do_flush();
21588c2ecf20Sopenharmony_ci				goto out;
21598c2ecf20Sopenharmony_ci			}
21608c2ecf20Sopenharmony_ci			if (up_flag)
21618c2ecf20Sopenharmony_ci				goto out;
21628c2ecf20Sopenharmony_ci			if (last_keycode == keycode &&
21638c2ecf20Sopenharmony_ci			    time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
21648c2ecf20Sopenharmony_ci				spk_close_press = 1;
21658c2ecf20Sopenharmony_ci				offset = spk_shift_table[shift_info + 32];
21668c2ecf20Sopenharmony_ci				/* double press? */
21678c2ecf20Sopenharmony_ci				if (offset && key_info[offset])
21688c2ecf20Sopenharmony_ci					new_key = key_info[offset];
21698c2ecf20Sopenharmony_ci			}
21708c2ecf20Sopenharmony_ci			last_keycode = keycode;
21718c2ecf20Sopenharmony_ci			last_spk_jiffy = jiffies;
21728c2ecf20Sopenharmony_ci			type = KT_SPKUP;
21738c2ecf20Sopenharmony_ci			value = new_key;
21748c2ecf20Sopenharmony_ci		}
21758c2ecf20Sopenharmony_ci	}
21768c2ecf20Sopenharmony_cino_map:
21778c2ecf20Sopenharmony_ci	if (type == KT_SPKUP && !spk_special_handler) {
21788c2ecf20Sopenharmony_ci		do_spkup(vc, new_key);
21798c2ecf20Sopenharmony_ci		spk_close_press = 0;
21808c2ecf20Sopenharmony_ci		ret = 1;
21818c2ecf20Sopenharmony_ci		goto out;
21828c2ecf20Sopenharmony_ci	}
21838c2ecf20Sopenharmony_ci	if (up_flag || spk_killed || type == KT_SHIFT)
21848c2ecf20Sopenharmony_ci		goto out;
21858c2ecf20Sopenharmony_ci	spk_shut_up &= 0xfe;
21868c2ecf20Sopenharmony_ci	kh = (value == KVAL(K_DOWN)) ||
21878c2ecf20Sopenharmony_ci	    (value == KVAL(K_UP)) ||
21888c2ecf20Sopenharmony_ci	    (value == KVAL(K_LEFT)) ||
21898c2ecf20Sopenharmony_ci	    (value == KVAL(K_RIGHT));
21908c2ecf20Sopenharmony_ci	if ((cursor_track != read_all_mode) || !kh)
21918c2ecf20Sopenharmony_ci		if (!spk_no_intr)
21928c2ecf20Sopenharmony_ci			spk_do_flush();
21938c2ecf20Sopenharmony_ci	if (spk_special_handler) {
21948c2ecf20Sopenharmony_ci		if (type == KT_SPEC && value == 1) {
21958c2ecf20Sopenharmony_ci			value = '\n';
21968c2ecf20Sopenharmony_ci			type = KT_LATIN;
21978c2ecf20Sopenharmony_ci		} else if (type == KT_LETTER) {
21988c2ecf20Sopenharmony_ci			type = KT_LATIN;
21998c2ecf20Sopenharmony_ci		} else if (value == 0x7f) {
22008c2ecf20Sopenharmony_ci			value = 8;	/* make del = backspace */
22018c2ecf20Sopenharmony_ci		}
22028c2ecf20Sopenharmony_ci		ret = (*spk_special_handler) (vc, type, value, keycode);
22038c2ecf20Sopenharmony_ci		spk_close_press = 0;
22048c2ecf20Sopenharmony_ci		if (ret < 0)
22058c2ecf20Sopenharmony_ci			bleep(9);
22068c2ecf20Sopenharmony_ci		goto out;
22078c2ecf20Sopenharmony_ci	}
22088c2ecf20Sopenharmony_ci	last_keycode = 0;
22098c2ecf20Sopenharmony_ciout:
22108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
22118c2ecf20Sopenharmony_ci	return ret;
22128c2ecf20Sopenharmony_ci}
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_cistatic int keyboard_notifier_call(struct notifier_block *nb,
22158c2ecf20Sopenharmony_ci				  unsigned long code, void *_param)
22168c2ecf20Sopenharmony_ci{
22178c2ecf20Sopenharmony_ci	struct keyboard_notifier_param *param = _param;
22188c2ecf20Sopenharmony_ci	struct vc_data *vc = param->vc;
22198c2ecf20Sopenharmony_ci	int up = !param->down;
22208c2ecf20Sopenharmony_ci	int ret = NOTIFY_OK;
22218c2ecf20Sopenharmony_ci	static int keycode;	/* to hold the current keycode */
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	in_keyboard_notifier = 1;
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	if (vc->vc_mode == KD_GRAPHICS)
22268c2ecf20Sopenharmony_ci		goto out;
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	/*
22298c2ecf20Sopenharmony_ci	 * First, determine whether we are handling a fake keypress on
22308c2ecf20Sopenharmony_ci	 * the current processor.  If we are, then return NOTIFY_OK,
22318c2ecf20Sopenharmony_ci	 * to pass the keystroke up the chain.  This prevents us from
22328c2ecf20Sopenharmony_ci	 * trying to take the Speakup lock while it is held by the
22338c2ecf20Sopenharmony_ci	 * processor on which the simulated keystroke was generated.
22348c2ecf20Sopenharmony_ci	 * Also, the simulated keystrokes should be ignored by Speakup.
22358c2ecf20Sopenharmony_ci	 */
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	if (speakup_fake_key_pressed())
22388c2ecf20Sopenharmony_ci		goto out;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	switch (code) {
22418c2ecf20Sopenharmony_ci	case KBD_KEYCODE:
22428c2ecf20Sopenharmony_ci		/* speakup requires keycode and keysym currently */
22438c2ecf20Sopenharmony_ci		keycode = param->value;
22448c2ecf20Sopenharmony_ci		break;
22458c2ecf20Sopenharmony_ci	case KBD_UNBOUND_KEYCODE:
22468c2ecf20Sopenharmony_ci		/* not used yet */
22478c2ecf20Sopenharmony_ci		break;
22488c2ecf20Sopenharmony_ci	case KBD_UNICODE:
22498c2ecf20Sopenharmony_ci		/* not used yet */
22508c2ecf20Sopenharmony_ci		break;
22518c2ecf20Sopenharmony_ci	case KBD_KEYSYM:
22528c2ecf20Sopenharmony_ci		if (speakup_key(vc, param->shift, keycode, param->value, up))
22538c2ecf20Sopenharmony_ci			ret = NOTIFY_STOP;
22548c2ecf20Sopenharmony_ci		else if (KTYP(param->value) == KT_CUR)
22558c2ecf20Sopenharmony_ci			ret = pre_handle_cursor(vc, KVAL(param->value), up);
22568c2ecf20Sopenharmony_ci		break;
22578c2ecf20Sopenharmony_ci	case KBD_POST_KEYSYM:{
22588c2ecf20Sopenharmony_ci			unsigned char type = KTYP(param->value) - 0xf0;
22598c2ecf20Sopenharmony_ci			unsigned char val = KVAL(param->value);
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci			switch (type) {
22628c2ecf20Sopenharmony_ci			case KT_SHIFT:
22638c2ecf20Sopenharmony_ci				do_handle_shift(vc, val, up);
22648c2ecf20Sopenharmony_ci				break;
22658c2ecf20Sopenharmony_ci			case KT_LATIN:
22668c2ecf20Sopenharmony_ci			case KT_LETTER:
22678c2ecf20Sopenharmony_ci				do_handle_latin(vc, val, up);
22688c2ecf20Sopenharmony_ci				break;
22698c2ecf20Sopenharmony_ci			case KT_CUR:
22708c2ecf20Sopenharmony_ci				do_handle_cursor(vc, val, up);
22718c2ecf20Sopenharmony_ci				break;
22728c2ecf20Sopenharmony_ci			case KT_SPEC:
22738c2ecf20Sopenharmony_ci				do_handle_spec(vc, val, up);
22748c2ecf20Sopenharmony_ci				break;
22758c2ecf20Sopenharmony_ci			}
22768c2ecf20Sopenharmony_ci			break;
22778c2ecf20Sopenharmony_ci		}
22788c2ecf20Sopenharmony_ci	}
22798c2ecf20Sopenharmony_ciout:
22808c2ecf20Sopenharmony_ci	in_keyboard_notifier = 0;
22818c2ecf20Sopenharmony_ci	return ret;
22828c2ecf20Sopenharmony_ci}
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_cistatic int vt_notifier_call(struct notifier_block *nb,
22858c2ecf20Sopenharmony_ci			    unsigned long code, void *_param)
22868c2ecf20Sopenharmony_ci{
22878c2ecf20Sopenharmony_ci	struct vt_notifier_param *param = _param;
22888c2ecf20Sopenharmony_ci	struct vc_data *vc = param->vc;
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci	switch (code) {
22918c2ecf20Sopenharmony_ci	case VT_ALLOCATE:
22928c2ecf20Sopenharmony_ci		if (vc->vc_mode == KD_TEXT)
22938c2ecf20Sopenharmony_ci			speakup_allocate(vc, GFP_ATOMIC);
22948c2ecf20Sopenharmony_ci		break;
22958c2ecf20Sopenharmony_ci	case VT_DEALLOCATE:
22968c2ecf20Sopenharmony_ci		speakup_deallocate(vc);
22978c2ecf20Sopenharmony_ci		break;
22988c2ecf20Sopenharmony_ci	case VT_WRITE:
22998c2ecf20Sopenharmony_ci		if (param->c == '\b') {
23008c2ecf20Sopenharmony_ci			speakup_bs(vc);
23018c2ecf20Sopenharmony_ci		} else {
23028c2ecf20Sopenharmony_ci			u16 d = param->c;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci			speakup_con_write(vc, &d, 1);
23058c2ecf20Sopenharmony_ci		}
23068c2ecf20Sopenharmony_ci		break;
23078c2ecf20Sopenharmony_ci	case VT_UPDATE:
23088c2ecf20Sopenharmony_ci		speakup_con_update(vc);
23098c2ecf20Sopenharmony_ci		break;
23108c2ecf20Sopenharmony_ci	}
23118c2ecf20Sopenharmony_ci	return NOTIFY_OK;
23128c2ecf20Sopenharmony_ci}
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci/* called by: module_exit() */
23158c2ecf20Sopenharmony_cistatic void __exit speakup_exit(void)
23168c2ecf20Sopenharmony_ci{
23178c2ecf20Sopenharmony_ci	int i;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	unregister_keyboard_notifier(&keyboard_notifier_block);
23208c2ecf20Sopenharmony_ci	unregister_vt_notifier(&vt_notifier_block);
23218c2ecf20Sopenharmony_ci	speakup_unregister_devsynth();
23228c2ecf20Sopenharmony_ci	speakup_cancel_selection();
23238c2ecf20Sopenharmony_ci	speakup_cancel_paste();
23248c2ecf20Sopenharmony_ci	del_timer_sync(&cursor_timer);
23258c2ecf20Sopenharmony_ci	kthread_stop(speakup_task);
23268c2ecf20Sopenharmony_ci	speakup_task = NULL;
23278c2ecf20Sopenharmony_ci	mutex_lock(&spk_mutex);
23288c2ecf20Sopenharmony_ci	synth_release();
23298c2ecf20Sopenharmony_ci	mutex_unlock(&spk_mutex);
23308c2ecf20Sopenharmony_ci	spk_ttyio_unregister_ldisc();
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_ci	speakup_kobj_exit();
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_NR_CONSOLES; i++)
23358c2ecf20Sopenharmony_ci		kfree(speakup_console[i]);
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci	speakup_remove_virtual_keyboard();
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	for (i = 0; i < MAXVARS; i++)
23408c2ecf20Sopenharmony_ci		speakup_unregister_var(i);
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++) {
23438c2ecf20Sopenharmony_ci		if (spk_characters[i] != spk_default_chars[i])
23448c2ecf20Sopenharmony_ci			kfree(spk_characters[i]);
23458c2ecf20Sopenharmony_ci	}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	spk_free_user_msgs();
23488c2ecf20Sopenharmony_ci}
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_ci/* call by: module_init() */
23518c2ecf20Sopenharmony_cistatic int __init speakup_init(void)
23528c2ecf20Sopenharmony_ci{
23538c2ecf20Sopenharmony_ci	int i;
23548c2ecf20Sopenharmony_ci	long err = 0;
23558c2ecf20Sopenharmony_ci	struct vc_data *vc = vc_cons[fg_console].d;
23568c2ecf20Sopenharmony_ci	struct var_t *var;
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci	/* These first few initializations cannot fail. */
23598c2ecf20Sopenharmony_ci	spk_initialize_msgs();	/* Initialize arrays for i18n. */
23608c2ecf20Sopenharmony_ci	spk_reset_default_chars();
23618c2ecf20Sopenharmony_ci	spk_reset_default_chartab();
23628c2ecf20Sopenharmony_ci	spk_strlwr(synth_name);
23638c2ecf20Sopenharmony_ci	spk_vars[0].u.n.high = vc->vc_cols;
23648c2ecf20Sopenharmony_ci	for (var = spk_vars; var->var_id != MAXVARS; var++)
23658c2ecf20Sopenharmony_ci		speakup_register_var(var);
23668c2ecf20Sopenharmony_ci	for (var = synth_time_vars;
23678c2ecf20Sopenharmony_ci	     (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
23688c2ecf20Sopenharmony_ci		speakup_register_var(var);
23698c2ecf20Sopenharmony_ci	for (i = 1; spk_punc_info[i].mask != 0; i++)
23708c2ecf20Sopenharmony_ci		spk_set_mask_bits(NULL, i, 2);
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci	spk_set_key_info(spk_key_defaults, spk_key_buf);
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	/* From here on out, initializations can fail. */
23758c2ecf20Sopenharmony_ci	err = speakup_add_virtual_keyboard();
23768c2ecf20Sopenharmony_ci	if (err)
23778c2ecf20Sopenharmony_ci		goto error_virtkeyboard;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_NR_CONSOLES; i++)
23808c2ecf20Sopenharmony_ci		if (vc_cons[i].d) {
23818c2ecf20Sopenharmony_ci			err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
23828c2ecf20Sopenharmony_ci			if (err)
23838c2ecf20Sopenharmony_ci				goto error_kobjects;
23848c2ecf20Sopenharmony_ci		}
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci	if (spk_quiet_boot)
23878c2ecf20Sopenharmony_ci		spk_shut_up |= 0x01;
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci	err = speakup_kobj_init();
23908c2ecf20Sopenharmony_ci	if (err)
23918c2ecf20Sopenharmony_ci		goto error_kobjects;
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	spk_ttyio_register_ldisc();
23948c2ecf20Sopenharmony_ci	synth_init(synth_name);
23958c2ecf20Sopenharmony_ci	speakup_register_devsynth();
23968c2ecf20Sopenharmony_ci	/*
23978c2ecf20Sopenharmony_ci	 * register_devsynth might fail, but this error is not fatal.
23988c2ecf20Sopenharmony_ci	 * /dev/synth is an extra feature; the rest of Speakup
23998c2ecf20Sopenharmony_ci	 * will work fine without it.
24008c2ecf20Sopenharmony_ci	 */
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci	err = register_keyboard_notifier(&keyboard_notifier_block);
24038c2ecf20Sopenharmony_ci	if (err)
24048c2ecf20Sopenharmony_ci		goto error_kbdnotifier;
24058c2ecf20Sopenharmony_ci	err = register_vt_notifier(&vt_notifier_block);
24068c2ecf20Sopenharmony_ci	if (err)
24078c2ecf20Sopenharmony_ci		goto error_vtnotifier;
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci	speakup_task = kthread_create(speakup_thread, NULL, "speakup");
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	if (IS_ERR(speakup_task)) {
24128c2ecf20Sopenharmony_ci		err = PTR_ERR(speakup_task);
24138c2ecf20Sopenharmony_ci		goto error_task;
24148c2ecf20Sopenharmony_ci	}
24158c2ecf20Sopenharmony_ci
24168c2ecf20Sopenharmony_ci	set_user_nice(speakup_task, 10);
24178c2ecf20Sopenharmony_ci	wake_up_process(speakup_task);
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
24208c2ecf20Sopenharmony_ci	pr_info("synth name on entry is: %s\n", synth_name);
24218c2ecf20Sopenharmony_ci	goto out;
24228c2ecf20Sopenharmony_ci
24238c2ecf20Sopenharmony_cierror_task:
24248c2ecf20Sopenharmony_ci	unregister_vt_notifier(&vt_notifier_block);
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_cierror_vtnotifier:
24278c2ecf20Sopenharmony_ci	unregister_keyboard_notifier(&keyboard_notifier_block);
24288c2ecf20Sopenharmony_ci	del_timer(&cursor_timer);
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_cierror_kbdnotifier:
24318c2ecf20Sopenharmony_ci	speakup_unregister_devsynth();
24328c2ecf20Sopenharmony_ci	mutex_lock(&spk_mutex);
24338c2ecf20Sopenharmony_ci	synth_release();
24348c2ecf20Sopenharmony_ci	mutex_unlock(&spk_mutex);
24358c2ecf20Sopenharmony_ci	speakup_kobj_exit();
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_cierror_kobjects:
24388c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_NR_CONSOLES; i++)
24398c2ecf20Sopenharmony_ci		kfree(speakup_console[i]);
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	speakup_remove_virtual_keyboard();
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_cierror_virtkeyboard:
24448c2ecf20Sopenharmony_ci	for (i = 0; i < MAXVARS; i++)
24458c2ecf20Sopenharmony_ci		speakup_unregister_var(i);
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	for (i = 0; i < 256; i++) {
24488c2ecf20Sopenharmony_ci		if (spk_characters[i] != spk_default_chars[i])
24498c2ecf20Sopenharmony_ci			kfree(spk_characters[i]);
24508c2ecf20Sopenharmony_ci	}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	spk_free_user_msgs();
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_ciout:
24558c2ecf20Sopenharmony_ci	return err;
24568c2ecf20Sopenharmony_ci}
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_cimodule_init(speakup_init);
24598c2ecf20Sopenharmony_cimodule_exit(speakup_exit);
2460