xref: /OK3568_Linux_fs/kernel/drivers/accessibility/speakup/main.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /* speakup.c
3*4882a593Smuzhiyun  * review functions for the speakup screen review package.
4*4882a593Smuzhiyun  * originally written by: Kirk Reiser and Andy Berdan.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * extensively modified by David Borowski.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  ** Copyright (C) 1998  Kirk Reiser.
9*4882a593Smuzhiyun  *  Copyright (C) 2003  David Borowski.
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/vt.h>
14*4882a593Smuzhiyun #include <linux/tty.h>
15*4882a593Smuzhiyun #include <linux/mm.h>		/* __get_free_page() and friends */
16*4882a593Smuzhiyun #include <linux/vt_kern.h>
17*4882a593Smuzhiyun #include <linux/ctype.h>
18*4882a593Smuzhiyun #include <linux/selection.h>
19*4882a593Smuzhiyun #include <linux/unistd.h>
20*4882a593Smuzhiyun #include <linux/jiffies.h>
21*4882a593Smuzhiyun #include <linux/kthread.h>
22*4882a593Smuzhiyun #include <linux/keyboard.h>	/* for KT_SHIFT */
23*4882a593Smuzhiyun #include <linux/kbd_kern.h>	/* for vc_kbd_* and friends */
24*4882a593Smuzhiyun #include <linux/input.h>
25*4882a593Smuzhiyun #include <linux/kmod.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun /* speakup_*_selection */
28*4882a593Smuzhiyun #include <linux/module.h>
29*4882a593Smuzhiyun #include <linux/sched.h>
30*4882a593Smuzhiyun #include <linux/slab.h>
31*4882a593Smuzhiyun #include <linux/types.h>
32*4882a593Smuzhiyun #include <linux/consolemap.h>
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #include <linux/spinlock.h>
35*4882a593Smuzhiyun #include <linux/notifier.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #include <linux/uaccess.h>	/* copy_from|to|user() and others */
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #include "spk_priv.h"
40*4882a593Smuzhiyun #include "speakup.h"
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #define MAX_DELAY msecs_to_jiffies(500)
43*4882a593Smuzhiyun #define MINECHOCHAR SPACE
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
46*4882a593Smuzhiyun MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
47*4882a593Smuzhiyun MODULE_DESCRIPTION("Speakup console speech");
48*4882a593Smuzhiyun MODULE_LICENSE("GPL");
49*4882a593Smuzhiyun MODULE_VERSION(SPEAKUP_VERSION);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun char *synth_name;
52*4882a593Smuzhiyun module_param_named(synth, synth_name, charp, 0444);
53*4882a593Smuzhiyun module_param_named(quiet, spk_quiet_boot, bool, 0444);
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
56*4882a593Smuzhiyun MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun special_func spk_special_handler;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun short spk_pitch_shift, synth_flags;
61*4882a593Smuzhiyun static u16 buf[256];
62*4882a593Smuzhiyun int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
63*4882a593Smuzhiyun int spk_no_intr, spk_spell_delay;
64*4882a593Smuzhiyun int spk_key_echo, spk_say_word_ctl;
65*4882a593Smuzhiyun int spk_say_ctrl, spk_bell_pos;
66*4882a593Smuzhiyun short spk_punc_mask;
67*4882a593Smuzhiyun int spk_punc_level, spk_reading_punc;
68*4882a593Smuzhiyun char spk_str_caps_start[MAXVARLEN + 1] = "\0";
69*4882a593Smuzhiyun char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
70*4882a593Smuzhiyun char spk_str_pause[MAXVARLEN + 1] = "\0";
71*4882a593Smuzhiyun bool spk_paused;
72*4882a593Smuzhiyun const struct st_bits_data spk_punc_info[] = {
73*4882a593Smuzhiyun 	{"none", "", 0},
74*4882a593Smuzhiyun 	{"some", "/$%&@", SOME},
75*4882a593Smuzhiyun 	{"most", "$%&#()=+*/@^<>|\\", MOST},
76*4882a593Smuzhiyun 	{"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
77*4882a593Smuzhiyun 	{"delimiters", "", B_WDLM},
78*4882a593Smuzhiyun 	{"repeats", "()", CH_RPT},
79*4882a593Smuzhiyun 	{"extended numeric", "", B_EXNUM},
80*4882a593Smuzhiyun 	{"symbols", "", B_SYM},
81*4882a593Smuzhiyun 	{NULL, NULL}
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun static char mark_cut_flag;
85*4882a593Smuzhiyun #define MAX_KEY 160
86*4882a593Smuzhiyun static u_char *spk_shift_table;
87*4882a593Smuzhiyun u_char *spk_our_keys[MAX_KEY];
88*4882a593Smuzhiyun u_char spk_key_buf[600];
89*4882a593Smuzhiyun const u_char spk_key_defaults[] = {
90*4882a593Smuzhiyun #include "speakupmap.h"
91*4882a593Smuzhiyun };
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun /* Speakup Cursor Track Variables */
94*4882a593Smuzhiyun static int cursor_track = 1, prev_cursor_track = 1;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun /* cursor track modes, must be ordered same as cursor_msgs */
97*4882a593Smuzhiyun enum {
98*4882a593Smuzhiyun 	CT_Off = 0,
99*4882a593Smuzhiyun 	CT_On,
100*4882a593Smuzhiyun 	CT_Highlight,
101*4882a593Smuzhiyun 	CT_Window,
102*4882a593Smuzhiyun 	CT_Max
103*4882a593Smuzhiyun };
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun #define read_all_mode CT_Max
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun static struct tty_struct *tty;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun static void spkup_write(const u16 *in_buf, int count);
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun static char *phonetic[] = {
112*4882a593Smuzhiyun 	"alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
113*4882a593Smuzhiyun 	"india", "juliett", "keelo", "leema", "mike", "november", "oscar",
114*4882a593Smuzhiyun 	    "papa",
115*4882a593Smuzhiyun 	"keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
116*4882a593Smuzhiyun 	"x ray", "yankee", "zulu"
117*4882a593Smuzhiyun };
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun /* array of 256 char pointers (one for each character description)
120*4882a593Smuzhiyun  * initialized to default_chars and user selectable via
121*4882a593Smuzhiyun  * /proc/speakup/characters
122*4882a593Smuzhiyun  */
123*4882a593Smuzhiyun char *spk_characters[256];
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun char *spk_default_chars[256] = {
126*4882a593Smuzhiyun /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
127*4882a593Smuzhiyun /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
128*4882a593Smuzhiyun /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
129*4882a593Smuzhiyun /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
130*4882a593Smuzhiyun 	    "control",
131*4882a593Smuzhiyun /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
132*4882a593Smuzhiyun 	    "tick",
133*4882a593Smuzhiyun /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
134*4882a593Smuzhiyun 	    "dot",
135*4882a593Smuzhiyun 	"slash",
136*4882a593Smuzhiyun /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
137*4882a593Smuzhiyun 	"eight", "nine",
138*4882a593Smuzhiyun /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
139*4882a593Smuzhiyun /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
140*4882a593Smuzhiyun /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
141*4882a593Smuzhiyun /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
142*4882a593Smuzhiyun /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
143*4882a593Smuzhiyun 	    "caret",
144*4882a593Smuzhiyun 	"line",
145*4882a593Smuzhiyun /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
146*4882a593Smuzhiyun /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
147*4882a593Smuzhiyun /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
148*4882a593Smuzhiyun /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
149*4882a593Smuzhiyun /*127*/ "del", "control", "control", "control", "control", "control",
150*4882a593Smuzhiyun 	    "control", "control", "control", "control", "control",
151*4882a593Smuzhiyun /*138*/ "control", "control", "control", "control", "control",
152*4882a593Smuzhiyun 	    "control", "control", "control", "control", "control",
153*4882a593Smuzhiyun 	    "control", "control",
154*4882a593Smuzhiyun /*150*/ "control", "control", "control", "control", "control",
155*4882a593Smuzhiyun 	    "control", "control", "control", "control", "control",
156*4882a593Smuzhiyun /*160*/ "nbsp", "inverted bang",
157*4882a593Smuzhiyun /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
158*4882a593Smuzhiyun /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
159*4882a593Smuzhiyun /*172*/ "not", "soft hyphen", "registered", "macron",
160*4882a593Smuzhiyun /*176*/ "degrees", "plus or minus", "super two", "super three",
161*4882a593Smuzhiyun /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
162*4882a593Smuzhiyun /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
163*4882a593Smuzhiyun /*188*/ "one quarter", "one half", "three quarters",
164*4882a593Smuzhiyun 	    "inverted question",
165*4882a593Smuzhiyun /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
166*4882a593Smuzhiyun 	    "A RING",
167*4882a593Smuzhiyun /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
168*4882a593Smuzhiyun 	    "E OOMLAUT",
169*4882a593Smuzhiyun /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
170*4882a593Smuzhiyun 	    "N TILDE",
171*4882a593Smuzhiyun /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
172*4882a593Smuzhiyun /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
173*4882a593Smuzhiyun 	    "U CIRCUMFLEX",
174*4882a593Smuzhiyun /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
175*4882a593Smuzhiyun /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
176*4882a593Smuzhiyun /*230*/ "ae", "c cidella", "e grave", "e acute",
177*4882a593Smuzhiyun /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
178*4882a593Smuzhiyun 	    "i circumflex",
179*4882a593Smuzhiyun /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
180*4882a593Smuzhiyun 	    "o circumflex",
181*4882a593Smuzhiyun /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
182*4882a593Smuzhiyun 	    "u acute",
183*4882a593Smuzhiyun /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
184*4882a593Smuzhiyun };
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun /* array of 256 u_short (one for each character)
187*4882a593Smuzhiyun  * initialized to default_chartab and user selectable via
188*4882a593Smuzhiyun  * /sys/module/speakup/parameters/chartab
189*4882a593Smuzhiyun  */
190*4882a593Smuzhiyun u_short spk_chartab[256];
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun static u_short default_chartab[256] = {
193*4882a593Smuzhiyun 	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 0-7 */
194*4882a593Smuzhiyun 	B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 8-15 */
195*4882a593Smuzhiyun 	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/*16-23 */
196*4882a593Smuzhiyun 	B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL,	/* 24-31 */
197*4882a593Smuzhiyun 	WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,	/*  !"#$%&' */
198*4882a593Smuzhiyun 	PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC,	/* ()*+, -./ */
199*4882a593Smuzhiyun 	NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM,	/* 01234567 */
200*4882a593Smuzhiyun 	NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC,	/* 89:;<=>? */
201*4882a593Smuzhiyun 	PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* @ABCDEFG */
202*4882a593Smuzhiyun 	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* HIJKLMNO */
203*4882a593Smuzhiyun 	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* PQRSTUVW */
204*4882a593Smuzhiyun 	A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC,	/* XYZ[\]^_ */
205*4882a593Smuzhiyun 	PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* `abcdefg */
206*4882a593Smuzhiyun 	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* hijklmno */
207*4882a593Smuzhiyun 	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* pqrstuvw */
208*4882a593Smuzhiyun 	ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0,	/* xyz{|}~ */
209*4882a593Smuzhiyun 	B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
210*4882a593Smuzhiyun 	B_SYM,	/* 135 */
211*4882a593Smuzhiyun 	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
212*4882a593Smuzhiyun 	B_CAPSYM,	/* 143 */
213*4882a593Smuzhiyun 	B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
214*4882a593Smuzhiyun 	B_SYM,	/* 151 */
215*4882a593Smuzhiyun 	B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
216*4882a593Smuzhiyun 	B_SYM,	/* 159 */
217*4882a593Smuzhiyun 	WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
218*4882a593Smuzhiyun 	B_SYM,	/* 167 */
219*4882a593Smuzhiyun 	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 168-175 */
220*4882a593Smuzhiyun 	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 176-183 */
221*4882a593Smuzhiyun 	B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM,	/* 184-191 */
222*4882a593Smuzhiyun 	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* 192-199 */
223*4882a593Smuzhiyun 	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP,	/* 200-207 */
224*4882a593Smuzhiyun 	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM,	/* 208-215 */
225*4882a593Smuzhiyun 	A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA,	/* 216-223 */
226*4882a593Smuzhiyun 	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* 224-231 */
227*4882a593Smuzhiyun 	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,	/* 232-239 */
228*4882a593Smuzhiyun 	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM,	/* 240-247 */
229*4882a593Smuzhiyun 	ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA	/* 248-255 */
230*4882a593Smuzhiyun };
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun struct task_struct *speakup_task;
233*4882a593Smuzhiyun struct bleep spk_unprocessed_sound;
234*4882a593Smuzhiyun static int spk_keydown;
235*4882a593Smuzhiyun static u16 spk_lastkey;
236*4882a593Smuzhiyun static u_char spk_close_press, keymap_flags;
237*4882a593Smuzhiyun static u_char last_keycode, this_speakup_key;
238*4882a593Smuzhiyun static u_long last_spk_jiffy;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun DEFINE_MUTEX(spk_mutex);
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun static int keyboard_notifier_call(struct notifier_block *,
245*4882a593Smuzhiyun 				  unsigned long code, void *param);
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun static struct notifier_block keyboard_notifier_block = {
248*4882a593Smuzhiyun 	.notifier_call = keyboard_notifier_call,
249*4882a593Smuzhiyun };
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun static int vt_notifier_call(struct notifier_block *,
252*4882a593Smuzhiyun 			    unsigned long code, void *param);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun static struct notifier_block vt_notifier_block = {
255*4882a593Smuzhiyun 	.notifier_call = vt_notifier_call,
256*4882a593Smuzhiyun };
257*4882a593Smuzhiyun 
get_attributes(struct vc_data * vc,u16 * pos)258*4882a593Smuzhiyun static unsigned char get_attributes(struct vc_data *vc, u16 *pos)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
261*4882a593Smuzhiyun 	return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun 
speakup_date(struct vc_data * vc)264*4882a593Smuzhiyun static void speakup_date(struct vc_data *vc)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun 	spk_x = spk_cx = vc->state.x;
267*4882a593Smuzhiyun 	spk_y = spk_cy = vc->state.y;
268*4882a593Smuzhiyun 	spk_pos = spk_cp = vc->vc_pos;
269*4882a593Smuzhiyun 	spk_old_attr = spk_attr;
270*4882a593Smuzhiyun 	spk_attr = get_attributes(vc, (u_short *)spk_pos);
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
bleep(u_short val)273*4882a593Smuzhiyun static void bleep(u_short val)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	static const short vals[] = {
276*4882a593Smuzhiyun 		350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
277*4882a593Smuzhiyun 	};
278*4882a593Smuzhiyun 	short freq;
279*4882a593Smuzhiyun 	int time = spk_bleep_time;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	freq = vals[val % 12];
282*4882a593Smuzhiyun 	if (val > 11)
283*4882a593Smuzhiyun 		freq *= (1 << (val / 12));
284*4882a593Smuzhiyun 	spk_unprocessed_sound.freq = freq;
285*4882a593Smuzhiyun 	spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
286*4882a593Smuzhiyun 	spk_unprocessed_sound.active = 1;
287*4882a593Smuzhiyun 	/* We can only have 1 active sound at a time. */
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun 
speakup_shut_up(struct vc_data * vc)290*4882a593Smuzhiyun static void speakup_shut_up(struct vc_data *vc)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun 	if (spk_killed)
293*4882a593Smuzhiyun 		return;
294*4882a593Smuzhiyun 	spk_shut_up |= 0x01;
295*4882a593Smuzhiyun 	spk_parked &= 0xfe;
296*4882a593Smuzhiyun 	speakup_date(vc);
297*4882a593Smuzhiyun 	if (synth)
298*4882a593Smuzhiyun 		spk_do_flush();
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
speech_kill(struct vc_data * vc)301*4882a593Smuzhiyun static void speech_kill(struct vc_data *vc)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun 	char val = synth->is_alive(synth);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	if (val == 0)
306*4882a593Smuzhiyun 		return;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	/* re-enables synth, if disabled */
309*4882a593Smuzhiyun 	if (val == 2 || spk_killed) {
310*4882a593Smuzhiyun 		/* dead */
311*4882a593Smuzhiyun 		spk_shut_up &= ~0x40;
312*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
313*4882a593Smuzhiyun 	} else {
314*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
315*4882a593Smuzhiyun 		spk_shut_up |= 0x40;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun }
318*4882a593Smuzhiyun 
speakup_off(struct vc_data * vc)319*4882a593Smuzhiyun static void speakup_off(struct vc_data *vc)
320*4882a593Smuzhiyun {
321*4882a593Smuzhiyun 	if (spk_shut_up & 0x80) {
322*4882a593Smuzhiyun 		spk_shut_up &= 0x7f;
323*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
324*4882a593Smuzhiyun 	} else {
325*4882a593Smuzhiyun 		spk_shut_up |= 0x80;
326*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
327*4882a593Smuzhiyun 	}
328*4882a593Smuzhiyun 	speakup_date(vc);
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun 
speakup_parked(struct vc_data * vc)331*4882a593Smuzhiyun static void speakup_parked(struct vc_data *vc)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	if (spk_parked & 0x80) {
334*4882a593Smuzhiyun 		spk_parked = 0;
335*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
336*4882a593Smuzhiyun 	} else {
337*4882a593Smuzhiyun 		spk_parked |= 0x80;
338*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_PARKED));
339*4882a593Smuzhiyun 	}
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun 
speakup_cut(struct vc_data * vc)342*4882a593Smuzhiyun static void speakup_cut(struct vc_data *vc)
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun 	static const char err_buf[] = "set selection failed";
345*4882a593Smuzhiyun 	int ret;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	if (!mark_cut_flag) {
348*4882a593Smuzhiyun 		mark_cut_flag = 1;
349*4882a593Smuzhiyun 		spk_xs = (u_short)spk_x;
350*4882a593Smuzhiyun 		spk_ys = (u_short)spk_y;
351*4882a593Smuzhiyun 		spk_sel_cons = vc;
352*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_MARK));
353*4882a593Smuzhiyun 		return;
354*4882a593Smuzhiyun 	}
355*4882a593Smuzhiyun 	spk_xe = (u_short)spk_x;
356*4882a593Smuzhiyun 	spk_ye = (u_short)spk_y;
357*4882a593Smuzhiyun 	mark_cut_flag = 0;
358*4882a593Smuzhiyun 	synth_printf("%s\n", spk_msg_get(MSG_CUT));
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	ret = speakup_set_selection(tty);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	switch (ret) {
363*4882a593Smuzhiyun 	case 0:
364*4882a593Smuzhiyun 		break;		/* no error */
365*4882a593Smuzhiyun 	case -EFAULT:
366*4882a593Smuzhiyun 		pr_warn("%sEFAULT\n", err_buf);
367*4882a593Smuzhiyun 		break;
368*4882a593Smuzhiyun 	case -EINVAL:
369*4882a593Smuzhiyun 		pr_warn("%sEINVAL\n", err_buf);
370*4882a593Smuzhiyun 		break;
371*4882a593Smuzhiyun 	case -ENOMEM:
372*4882a593Smuzhiyun 		pr_warn("%sENOMEM\n", err_buf);
373*4882a593Smuzhiyun 		break;
374*4882a593Smuzhiyun 	}
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun 
speakup_paste(struct vc_data * vc)377*4882a593Smuzhiyun static void speakup_paste(struct vc_data *vc)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun 	if (mark_cut_flag) {
380*4882a593Smuzhiyun 		mark_cut_flag = 0;
381*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
382*4882a593Smuzhiyun 	} else {
383*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_PASTE));
384*4882a593Smuzhiyun 		speakup_paste_selection(tty);
385*4882a593Smuzhiyun 	}
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun 
say_attributes(struct vc_data * vc)388*4882a593Smuzhiyun static void say_attributes(struct vc_data *vc)
389*4882a593Smuzhiyun {
390*4882a593Smuzhiyun 	int fg = spk_attr & 0x0f;
391*4882a593Smuzhiyun 	int bg = spk_attr >> 4;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	if (fg > 8) {
394*4882a593Smuzhiyun 		synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
395*4882a593Smuzhiyun 		fg -= 8;
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 	synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
398*4882a593Smuzhiyun 	if (bg > 7) {
399*4882a593Smuzhiyun 		synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
400*4882a593Smuzhiyun 		bg -= 8;
401*4882a593Smuzhiyun 	} else {
402*4882a593Smuzhiyun 		synth_printf(" %s ", spk_msg_get(MSG_ON));
403*4882a593Smuzhiyun 	}
404*4882a593Smuzhiyun 	synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun enum {
408*4882a593Smuzhiyun 	edge_top = 1,
409*4882a593Smuzhiyun 	edge_bottom,
410*4882a593Smuzhiyun 	edge_left,
411*4882a593Smuzhiyun 	edge_right,
412*4882a593Smuzhiyun 	edge_quiet
413*4882a593Smuzhiyun };
414*4882a593Smuzhiyun 
announce_edge(struct vc_data * vc,int msg_id)415*4882a593Smuzhiyun static void announce_edge(struct vc_data *vc, int msg_id)
416*4882a593Smuzhiyun {
417*4882a593Smuzhiyun 	if (spk_bleeps & 1)
418*4882a593Smuzhiyun 		bleep(spk_y);
419*4882a593Smuzhiyun 	if ((spk_bleeps & 2) && (msg_id < edge_quiet))
420*4882a593Smuzhiyun 		synth_printf("%s\n",
421*4882a593Smuzhiyun 			     spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun 
speak_char(u16 ch)424*4882a593Smuzhiyun static void speak_char(u16 ch)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun 	char *cp;
427*4882a593Smuzhiyun 	struct var_t *direct = spk_get_var(DIRECT);
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	if (ch >= 0x100 || (direct && direct->u.n.value)) {
430*4882a593Smuzhiyun 		if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
431*4882a593Smuzhiyun 			spk_pitch_shift++;
432*4882a593Smuzhiyun 			synth_printf("%s", spk_str_caps_start);
433*4882a593Smuzhiyun 		}
434*4882a593Smuzhiyun 		synth_putwc_s(ch);
435*4882a593Smuzhiyun 		if (ch < 0x100 && IS_CHAR(ch, B_CAP))
436*4882a593Smuzhiyun 			synth_printf("%s", spk_str_caps_stop);
437*4882a593Smuzhiyun 		return;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	cp = spk_characters[ch];
441*4882a593Smuzhiyun 	if (!cp) {
442*4882a593Smuzhiyun 		pr_info("%s: cp == NULL!\n", __func__);
443*4882a593Smuzhiyun 		return;
444*4882a593Smuzhiyun 	}
445*4882a593Smuzhiyun 	if (IS_CHAR(ch, B_CAP)) {
446*4882a593Smuzhiyun 		spk_pitch_shift++;
447*4882a593Smuzhiyun 		synth_printf("%s %s %s",
448*4882a593Smuzhiyun 			     spk_str_caps_start, cp, spk_str_caps_stop);
449*4882a593Smuzhiyun 	} else {
450*4882a593Smuzhiyun 		if (*cp == '^') {
451*4882a593Smuzhiyun 			cp++;
452*4882a593Smuzhiyun 			synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);
453*4882a593Smuzhiyun 		} else {
454*4882a593Smuzhiyun 			synth_printf(" %s ", cp);
455*4882a593Smuzhiyun 		}
456*4882a593Smuzhiyun 	}
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun 
get_char(struct vc_data * vc,u16 * pos,u_char * attribs)459*4882a593Smuzhiyun static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
460*4882a593Smuzhiyun {
461*4882a593Smuzhiyun 	u16 ch = ' ';
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	if (vc && pos) {
464*4882a593Smuzhiyun 		u16 w;
465*4882a593Smuzhiyun 		u16 c;
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 		pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
468*4882a593Smuzhiyun 		w = scr_readw(pos);
469*4882a593Smuzhiyun 		c = w & 0xff;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 		if (w & vc->vc_hi_font_mask) {
472*4882a593Smuzhiyun 			w &= ~vc->vc_hi_font_mask;
473*4882a593Smuzhiyun 			c |= 0x100;
474*4882a593Smuzhiyun 		}
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 		ch = inverse_translate(vc, c, 1);
477*4882a593Smuzhiyun 		*attribs = (w & 0xff00) >> 8;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 	return ch;
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun 
say_char(struct vc_data * vc)482*4882a593Smuzhiyun static void say_char(struct vc_data *vc)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun 	u16 ch;
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	spk_old_attr = spk_attr;
487*4882a593Smuzhiyun 	ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
488*4882a593Smuzhiyun 	if (spk_attr != spk_old_attr) {
489*4882a593Smuzhiyun 		if (spk_attrib_bleep & 1)
490*4882a593Smuzhiyun 			bleep(spk_y);
491*4882a593Smuzhiyun 		if (spk_attrib_bleep & 2)
492*4882a593Smuzhiyun 			say_attributes(vc);
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 	speak_char(ch);
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun 
say_phonetic_char(struct vc_data * vc)497*4882a593Smuzhiyun static void say_phonetic_char(struct vc_data *vc)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 	u16 ch;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	spk_old_attr = spk_attr;
502*4882a593Smuzhiyun 	ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
503*4882a593Smuzhiyun 	if (ch <= 0x7f && isalpha(ch)) {
504*4882a593Smuzhiyun 		ch &= 0x1f;
505*4882a593Smuzhiyun 		synth_printf("%s\n", phonetic[--ch]);
506*4882a593Smuzhiyun 	} else {
507*4882a593Smuzhiyun 		if (ch < 0x100 && IS_CHAR(ch, B_NUM))
508*4882a593Smuzhiyun 			synth_printf("%s ", spk_msg_get(MSG_NUMBER));
509*4882a593Smuzhiyun 		speak_char(ch);
510*4882a593Smuzhiyun 	}
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun 
say_prev_char(struct vc_data * vc)513*4882a593Smuzhiyun static void say_prev_char(struct vc_data *vc)
514*4882a593Smuzhiyun {
515*4882a593Smuzhiyun 	spk_parked |= 0x01;
516*4882a593Smuzhiyun 	if (spk_x == 0) {
517*4882a593Smuzhiyun 		announce_edge(vc, edge_left);
518*4882a593Smuzhiyun 		return;
519*4882a593Smuzhiyun 	}
520*4882a593Smuzhiyun 	spk_x--;
521*4882a593Smuzhiyun 	spk_pos -= 2;
522*4882a593Smuzhiyun 	say_char(vc);
523*4882a593Smuzhiyun }
524*4882a593Smuzhiyun 
say_next_char(struct vc_data * vc)525*4882a593Smuzhiyun static void say_next_char(struct vc_data *vc)
526*4882a593Smuzhiyun {
527*4882a593Smuzhiyun 	spk_parked |= 0x01;
528*4882a593Smuzhiyun 	if (spk_x == vc->vc_cols - 1) {
529*4882a593Smuzhiyun 		announce_edge(vc, edge_right);
530*4882a593Smuzhiyun 		return;
531*4882a593Smuzhiyun 	}
532*4882a593Smuzhiyun 	spk_x++;
533*4882a593Smuzhiyun 	spk_pos += 2;
534*4882a593Smuzhiyun 	say_char(vc);
535*4882a593Smuzhiyun }
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun /* get_word - will first check to see if the character under the
538*4882a593Smuzhiyun  * reading cursor is a space and if spk_say_word_ctl is true it will
539*4882a593Smuzhiyun  * return the word space.  If spk_say_word_ctl is not set it will check to
540*4882a593Smuzhiyun  * see if there is a word starting on the next position to the right
541*4882a593Smuzhiyun  * and return that word if it exists.  If it does not exist it will
542*4882a593Smuzhiyun  * move left to the beginning of any previous word on the line or the
543*4882a593Smuzhiyun  * beginning off the line whichever comes first..
544*4882a593Smuzhiyun  */
545*4882a593Smuzhiyun 
get_word(struct vc_data * vc)546*4882a593Smuzhiyun static u_long get_word(struct vc_data *vc)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun 	u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
549*4882a593Smuzhiyun 	u16 ch;
550*4882a593Smuzhiyun 	u16 attr_ch;
551*4882a593Smuzhiyun 	u_char temp;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	spk_old_attr = spk_attr;
554*4882a593Smuzhiyun 	ch = get_char(vc, (u_short *)tmp_pos, &temp);
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun /* decided to take out the sayword if on a space (mis-information */
557*4882a593Smuzhiyun 	if (spk_say_word_ctl && ch == SPACE) {
558*4882a593Smuzhiyun 		*buf = '\0';
559*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_SPACE));
560*4882a593Smuzhiyun 		return 0;
561*4882a593Smuzhiyun 	} else if (tmpx < vc->vc_cols - 2 &&
562*4882a593Smuzhiyun 		   (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
563*4882a593Smuzhiyun 		   get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
564*4882a593Smuzhiyun 		tmp_pos += 2;
565*4882a593Smuzhiyun 		tmpx++;
566*4882a593Smuzhiyun 	} else {
567*4882a593Smuzhiyun 		while (tmpx > 0) {
568*4882a593Smuzhiyun 			ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
569*4882a593Smuzhiyun 			if ((ch == SPACE || ch == 0 ||
570*4882a593Smuzhiyun 			     (ch < 0x100 && IS_WDLM(ch))) &&
571*4882a593Smuzhiyun 			    get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
572*4882a593Smuzhiyun 				break;
573*4882a593Smuzhiyun 			tmp_pos -= 2;
574*4882a593Smuzhiyun 			tmpx--;
575*4882a593Smuzhiyun 		}
576*4882a593Smuzhiyun 	}
577*4882a593Smuzhiyun 	attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
578*4882a593Smuzhiyun 	buf[cnt++] = attr_ch;
579*4882a593Smuzhiyun 	while (tmpx < vc->vc_cols - 1) {
580*4882a593Smuzhiyun 		tmp_pos += 2;
581*4882a593Smuzhiyun 		tmpx++;
582*4882a593Smuzhiyun 		ch = get_char(vc, (u_short *)tmp_pos, &temp);
583*4882a593Smuzhiyun 		if (ch == SPACE || ch == 0 ||
584*4882a593Smuzhiyun 		    (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
585*4882a593Smuzhiyun 		     ch > SPACE))
586*4882a593Smuzhiyun 			break;
587*4882a593Smuzhiyun 		buf[cnt++] = ch;
588*4882a593Smuzhiyun 	}
589*4882a593Smuzhiyun 	buf[cnt] = '\0';
590*4882a593Smuzhiyun 	return cnt;
591*4882a593Smuzhiyun }
592*4882a593Smuzhiyun 
say_word(struct vc_data * vc)593*4882a593Smuzhiyun static void say_word(struct vc_data *vc)
594*4882a593Smuzhiyun {
595*4882a593Smuzhiyun 	u_long cnt = get_word(vc);
596*4882a593Smuzhiyun 	u_short saved_punc_mask = spk_punc_mask;
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	if (cnt == 0)
599*4882a593Smuzhiyun 		return;
600*4882a593Smuzhiyun 	spk_punc_mask = PUNC;
601*4882a593Smuzhiyun 	buf[cnt++] = SPACE;
602*4882a593Smuzhiyun 	spkup_write(buf, cnt);
603*4882a593Smuzhiyun 	spk_punc_mask = saved_punc_mask;
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun 
say_prev_word(struct vc_data * vc)606*4882a593Smuzhiyun static void say_prev_word(struct vc_data *vc)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	u_char temp;
609*4882a593Smuzhiyun 	u16 ch;
610*4882a593Smuzhiyun 	u_short edge_said = 0, last_state = 0, state = 0;
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	spk_parked |= 0x01;
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun 	if (spk_x == 0) {
615*4882a593Smuzhiyun 		if (spk_y == 0) {
616*4882a593Smuzhiyun 			announce_edge(vc, edge_top);
617*4882a593Smuzhiyun 			return;
618*4882a593Smuzhiyun 		}
619*4882a593Smuzhiyun 		spk_y--;
620*4882a593Smuzhiyun 		spk_x = vc->vc_cols;
621*4882a593Smuzhiyun 		edge_said = edge_quiet;
622*4882a593Smuzhiyun 	}
623*4882a593Smuzhiyun 	while (1) {
624*4882a593Smuzhiyun 		if (spk_x == 0) {
625*4882a593Smuzhiyun 			if (spk_y == 0) {
626*4882a593Smuzhiyun 				edge_said = edge_top;
627*4882a593Smuzhiyun 				break;
628*4882a593Smuzhiyun 			}
629*4882a593Smuzhiyun 			if (edge_said != edge_quiet)
630*4882a593Smuzhiyun 				edge_said = edge_left;
631*4882a593Smuzhiyun 			if (state > 0)
632*4882a593Smuzhiyun 				break;
633*4882a593Smuzhiyun 			spk_y--;
634*4882a593Smuzhiyun 			spk_x = vc->vc_cols - 1;
635*4882a593Smuzhiyun 		} else {
636*4882a593Smuzhiyun 			spk_x--;
637*4882a593Smuzhiyun 		}
638*4882a593Smuzhiyun 		spk_pos -= 2;
639*4882a593Smuzhiyun 		ch = get_char(vc, (u_short *)spk_pos, &temp);
640*4882a593Smuzhiyun 		if (ch == SPACE || ch == 0)
641*4882a593Smuzhiyun 			state = 0;
642*4882a593Smuzhiyun 		else if (ch < 0x100 && IS_WDLM(ch))
643*4882a593Smuzhiyun 			state = 1;
644*4882a593Smuzhiyun 		else
645*4882a593Smuzhiyun 			state = 2;
646*4882a593Smuzhiyun 		if (state < last_state) {
647*4882a593Smuzhiyun 			spk_pos += 2;
648*4882a593Smuzhiyun 			spk_x++;
649*4882a593Smuzhiyun 			break;
650*4882a593Smuzhiyun 		}
651*4882a593Smuzhiyun 		last_state = state;
652*4882a593Smuzhiyun 	}
653*4882a593Smuzhiyun 	if (spk_x == 0 && edge_said == edge_quiet)
654*4882a593Smuzhiyun 		edge_said = edge_left;
655*4882a593Smuzhiyun 	if (edge_said > 0 && edge_said < edge_quiet)
656*4882a593Smuzhiyun 		announce_edge(vc, edge_said);
657*4882a593Smuzhiyun 	say_word(vc);
658*4882a593Smuzhiyun }
659*4882a593Smuzhiyun 
say_next_word(struct vc_data * vc)660*4882a593Smuzhiyun static void say_next_word(struct vc_data *vc)
661*4882a593Smuzhiyun {
662*4882a593Smuzhiyun 	u_char temp;
663*4882a593Smuzhiyun 	u16 ch;
664*4882a593Smuzhiyun 	u_short edge_said = 0, last_state = 2, state = 0;
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun 	spk_parked |= 0x01;
667*4882a593Smuzhiyun 	if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
668*4882a593Smuzhiyun 		announce_edge(vc, edge_bottom);
669*4882a593Smuzhiyun 		return;
670*4882a593Smuzhiyun 	}
671*4882a593Smuzhiyun 	while (1) {
672*4882a593Smuzhiyun 		ch = get_char(vc, (u_short *)spk_pos, &temp);
673*4882a593Smuzhiyun 		if (ch == SPACE || ch == 0)
674*4882a593Smuzhiyun 			state = 0;
675*4882a593Smuzhiyun 		else if (ch < 0x100 && IS_WDLM(ch))
676*4882a593Smuzhiyun 			state = 1;
677*4882a593Smuzhiyun 		else
678*4882a593Smuzhiyun 			state = 2;
679*4882a593Smuzhiyun 		if (state > last_state)
680*4882a593Smuzhiyun 			break;
681*4882a593Smuzhiyun 		if (spk_x >= vc->vc_cols - 1) {
682*4882a593Smuzhiyun 			if (spk_y == vc->vc_rows - 1) {
683*4882a593Smuzhiyun 				edge_said = edge_bottom;
684*4882a593Smuzhiyun 				break;
685*4882a593Smuzhiyun 			}
686*4882a593Smuzhiyun 			state = 0;
687*4882a593Smuzhiyun 			spk_y++;
688*4882a593Smuzhiyun 			spk_x = 0;
689*4882a593Smuzhiyun 			edge_said = edge_right;
690*4882a593Smuzhiyun 		} else {
691*4882a593Smuzhiyun 			spk_x++;
692*4882a593Smuzhiyun 		}
693*4882a593Smuzhiyun 		spk_pos += 2;
694*4882a593Smuzhiyun 		last_state = state;
695*4882a593Smuzhiyun 	}
696*4882a593Smuzhiyun 	if (edge_said > 0)
697*4882a593Smuzhiyun 		announce_edge(vc, edge_said);
698*4882a593Smuzhiyun 	say_word(vc);
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun 
spell_word(struct vc_data * vc)701*4882a593Smuzhiyun static void spell_word(struct vc_data *vc)
702*4882a593Smuzhiyun {
703*4882a593Smuzhiyun 	static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
704*4882a593Smuzhiyun 	u16 *cp = buf;
705*4882a593Smuzhiyun 	char *cp1;
706*4882a593Smuzhiyun 	char *str_cap = spk_str_caps_stop;
707*4882a593Smuzhiyun 	char *last_cap = spk_str_caps_stop;
708*4882a593Smuzhiyun 	struct var_t *direct = spk_get_var(DIRECT);
709*4882a593Smuzhiyun 	u16 ch;
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun 	if (!get_word(vc))
712*4882a593Smuzhiyun 		return;
713*4882a593Smuzhiyun 	while ((ch = *cp)) {
714*4882a593Smuzhiyun 		if (cp != buf)
715*4882a593Smuzhiyun 			synth_printf(" %s ", delay_str[spk_spell_delay]);
716*4882a593Smuzhiyun 		/* FIXME: Non-latin1 considered as lower case */
717*4882a593Smuzhiyun 		if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
718*4882a593Smuzhiyun 			str_cap = spk_str_caps_start;
719*4882a593Smuzhiyun 			if (*spk_str_caps_stop)
720*4882a593Smuzhiyun 				spk_pitch_shift++;
721*4882a593Smuzhiyun 			else	/* synth has no pitch */
722*4882a593Smuzhiyun 				last_cap = spk_str_caps_stop;
723*4882a593Smuzhiyun 		} else {
724*4882a593Smuzhiyun 			str_cap = spk_str_caps_stop;
725*4882a593Smuzhiyun 		}
726*4882a593Smuzhiyun 		if (str_cap != last_cap) {
727*4882a593Smuzhiyun 			synth_printf("%s", str_cap);
728*4882a593Smuzhiyun 			last_cap = str_cap;
729*4882a593Smuzhiyun 		}
730*4882a593Smuzhiyun 		if (ch >= 0x100 || (direct && direct->u.n.value)) {
731*4882a593Smuzhiyun 			synth_putwc_s(ch);
732*4882a593Smuzhiyun 		} else if (this_speakup_key == SPELL_PHONETIC &&
733*4882a593Smuzhiyun 		    ch <= 0x7f && isalpha(ch)) {
734*4882a593Smuzhiyun 			ch &= 0x1f;
735*4882a593Smuzhiyun 			cp1 = phonetic[--ch];
736*4882a593Smuzhiyun 			synth_printf("%s", cp1);
737*4882a593Smuzhiyun 		} else {
738*4882a593Smuzhiyun 			cp1 = spk_characters[ch];
739*4882a593Smuzhiyun 			if (*cp1 == '^') {
740*4882a593Smuzhiyun 				synth_printf("%s", spk_msg_get(MSG_CTRL));
741*4882a593Smuzhiyun 				cp1++;
742*4882a593Smuzhiyun 			}
743*4882a593Smuzhiyun 			synth_printf("%s", cp1);
744*4882a593Smuzhiyun 		}
745*4882a593Smuzhiyun 		cp++;
746*4882a593Smuzhiyun 	}
747*4882a593Smuzhiyun 	if (str_cap != spk_str_caps_stop)
748*4882a593Smuzhiyun 		synth_printf("%s", spk_str_caps_stop);
749*4882a593Smuzhiyun }
750*4882a593Smuzhiyun 
get_line(struct vc_data * vc)751*4882a593Smuzhiyun static int get_line(struct vc_data *vc)
752*4882a593Smuzhiyun {
753*4882a593Smuzhiyun 	u_long tmp = spk_pos - (spk_x * 2);
754*4882a593Smuzhiyun 	int i = 0;
755*4882a593Smuzhiyun 	u_char tmp2;
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	spk_old_attr = spk_attr;
758*4882a593Smuzhiyun 	spk_attr = get_attributes(vc, (u_short *)spk_pos);
759*4882a593Smuzhiyun 	for (i = 0; i < vc->vc_cols; i++) {
760*4882a593Smuzhiyun 		buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
761*4882a593Smuzhiyun 		tmp += 2;
762*4882a593Smuzhiyun 	}
763*4882a593Smuzhiyun 	for (--i; i >= 0; i--)
764*4882a593Smuzhiyun 		if (buf[i] != SPACE)
765*4882a593Smuzhiyun 			break;
766*4882a593Smuzhiyun 	return ++i;
767*4882a593Smuzhiyun }
768*4882a593Smuzhiyun 
say_line(struct vc_data * vc)769*4882a593Smuzhiyun static void say_line(struct vc_data *vc)
770*4882a593Smuzhiyun {
771*4882a593Smuzhiyun 	int i = get_line(vc);
772*4882a593Smuzhiyun 	u16 *cp;
773*4882a593Smuzhiyun 	u_short saved_punc_mask = spk_punc_mask;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	if (i == 0) {
776*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_BLANK));
777*4882a593Smuzhiyun 		return;
778*4882a593Smuzhiyun 	}
779*4882a593Smuzhiyun 	buf[i++] = '\n';
780*4882a593Smuzhiyun 	if (this_speakup_key == SAY_LINE_INDENT) {
781*4882a593Smuzhiyun 		cp = buf;
782*4882a593Smuzhiyun 		while (*cp == SPACE)
783*4882a593Smuzhiyun 			cp++;
784*4882a593Smuzhiyun 		synth_printf("%zd, ", (cp - buf) + 1);
785*4882a593Smuzhiyun 	}
786*4882a593Smuzhiyun 	spk_punc_mask = spk_punc_masks[spk_reading_punc];
787*4882a593Smuzhiyun 	spkup_write(buf, i);
788*4882a593Smuzhiyun 	spk_punc_mask = saved_punc_mask;
789*4882a593Smuzhiyun }
790*4882a593Smuzhiyun 
say_prev_line(struct vc_data * vc)791*4882a593Smuzhiyun static void say_prev_line(struct vc_data *vc)
792*4882a593Smuzhiyun {
793*4882a593Smuzhiyun 	spk_parked |= 0x01;
794*4882a593Smuzhiyun 	if (spk_y == 0) {
795*4882a593Smuzhiyun 		announce_edge(vc, edge_top);
796*4882a593Smuzhiyun 		return;
797*4882a593Smuzhiyun 	}
798*4882a593Smuzhiyun 	spk_y--;
799*4882a593Smuzhiyun 	spk_pos -= vc->vc_size_row;
800*4882a593Smuzhiyun 	say_line(vc);
801*4882a593Smuzhiyun }
802*4882a593Smuzhiyun 
say_next_line(struct vc_data * vc)803*4882a593Smuzhiyun static void say_next_line(struct vc_data *vc)
804*4882a593Smuzhiyun {
805*4882a593Smuzhiyun 	spk_parked |= 0x01;
806*4882a593Smuzhiyun 	if (spk_y == vc->vc_rows - 1) {
807*4882a593Smuzhiyun 		announce_edge(vc, edge_bottom);
808*4882a593Smuzhiyun 		return;
809*4882a593Smuzhiyun 	}
810*4882a593Smuzhiyun 	spk_y++;
811*4882a593Smuzhiyun 	spk_pos += vc->vc_size_row;
812*4882a593Smuzhiyun 	say_line(vc);
813*4882a593Smuzhiyun }
814*4882a593Smuzhiyun 
say_from_to(struct vc_data * vc,u_long from,u_long to,int read_punc)815*4882a593Smuzhiyun static int say_from_to(struct vc_data *vc, u_long from, u_long to,
816*4882a593Smuzhiyun 		       int read_punc)
817*4882a593Smuzhiyun {
818*4882a593Smuzhiyun 	int i = 0;
819*4882a593Smuzhiyun 	u_char tmp;
820*4882a593Smuzhiyun 	u_short saved_punc_mask = spk_punc_mask;
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 	spk_old_attr = spk_attr;
823*4882a593Smuzhiyun 	spk_attr = get_attributes(vc, (u_short *)from);
824*4882a593Smuzhiyun 	while (from < to) {
825*4882a593Smuzhiyun 		buf[i++] = get_char(vc, (u_short *)from, &tmp);
826*4882a593Smuzhiyun 		from += 2;
827*4882a593Smuzhiyun 		if (i >= vc->vc_size_row)
828*4882a593Smuzhiyun 			break;
829*4882a593Smuzhiyun 	}
830*4882a593Smuzhiyun 	for (--i; i >= 0; i--)
831*4882a593Smuzhiyun 		if (buf[i] != SPACE)
832*4882a593Smuzhiyun 			break;
833*4882a593Smuzhiyun 	buf[++i] = SPACE;
834*4882a593Smuzhiyun 	buf[++i] = '\0';
835*4882a593Smuzhiyun 	if (i < 1)
836*4882a593Smuzhiyun 		return i;
837*4882a593Smuzhiyun 	if (read_punc)
838*4882a593Smuzhiyun 		spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
839*4882a593Smuzhiyun 	spkup_write(buf, i);
840*4882a593Smuzhiyun 	if (read_punc)
841*4882a593Smuzhiyun 		spk_punc_mask = saved_punc_mask;
842*4882a593Smuzhiyun 	return i - 1;
843*4882a593Smuzhiyun }
844*4882a593Smuzhiyun 
say_line_from_to(struct vc_data * vc,u_long from,u_long to,int read_punc)845*4882a593Smuzhiyun static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
846*4882a593Smuzhiyun 			     int read_punc)
847*4882a593Smuzhiyun {
848*4882a593Smuzhiyun 	u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
849*4882a593Smuzhiyun 	u_long end = start + (to * 2);
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	start += from * 2;
852*4882a593Smuzhiyun 	if (say_from_to(vc, start, end, read_punc) <= 0)
853*4882a593Smuzhiyun 		if (cursor_track != read_all_mode)
854*4882a593Smuzhiyun 			synth_printf("%s\n", spk_msg_get(MSG_BLANK));
855*4882a593Smuzhiyun }
856*4882a593Smuzhiyun 
857*4882a593Smuzhiyun /* Sentence Reading Commands */
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun static int currsentence;
860*4882a593Smuzhiyun static int numsentences[2];
861*4882a593Smuzhiyun static u16 *sentbufend[2];
862*4882a593Smuzhiyun static u16 *sentmarks[2][10];
863*4882a593Smuzhiyun static int currbuf;
864*4882a593Smuzhiyun static int bn;
865*4882a593Smuzhiyun static u16 sentbuf[2][256];
866*4882a593Smuzhiyun 
say_sentence_num(int num,int prev)867*4882a593Smuzhiyun static int say_sentence_num(int num, int prev)
868*4882a593Smuzhiyun {
869*4882a593Smuzhiyun 	bn = currbuf;
870*4882a593Smuzhiyun 	currsentence = num + 1;
871*4882a593Smuzhiyun 	if (prev && --bn == -1)
872*4882a593Smuzhiyun 		bn = 1;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	if (num > numsentences[bn])
875*4882a593Smuzhiyun 		return 0;
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun 	spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
878*4882a593Smuzhiyun 	return 1;
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun 
get_sentence_buf(struct vc_data * vc,int read_punc)881*4882a593Smuzhiyun static int get_sentence_buf(struct vc_data *vc, int read_punc)
882*4882a593Smuzhiyun {
883*4882a593Smuzhiyun 	u_long start, end;
884*4882a593Smuzhiyun 	int i, bn;
885*4882a593Smuzhiyun 	u_char tmp;
886*4882a593Smuzhiyun 
887*4882a593Smuzhiyun 	currbuf++;
888*4882a593Smuzhiyun 	if (currbuf == 2)
889*4882a593Smuzhiyun 		currbuf = 0;
890*4882a593Smuzhiyun 	bn = currbuf;
891*4882a593Smuzhiyun 	start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
892*4882a593Smuzhiyun 	end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
893*4882a593Smuzhiyun 
894*4882a593Smuzhiyun 	numsentences[bn] = 0;
895*4882a593Smuzhiyun 	sentmarks[bn][0] = &sentbuf[bn][0];
896*4882a593Smuzhiyun 	i = 0;
897*4882a593Smuzhiyun 	spk_old_attr = spk_attr;
898*4882a593Smuzhiyun 	spk_attr = get_attributes(vc, (u_short *)start);
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun 	while (start < end) {
901*4882a593Smuzhiyun 		sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
902*4882a593Smuzhiyun 		if (i > 0) {
903*4882a593Smuzhiyun 			if (sentbuf[bn][i] == SPACE &&
904*4882a593Smuzhiyun 			    sentbuf[bn][i - 1] == '.' &&
905*4882a593Smuzhiyun 			    numsentences[bn] < 9) {
906*4882a593Smuzhiyun 				/* Sentence Marker */
907*4882a593Smuzhiyun 				numsentences[bn]++;
908*4882a593Smuzhiyun 				sentmarks[bn][numsentences[bn]] =
909*4882a593Smuzhiyun 				    &sentbuf[bn][i];
910*4882a593Smuzhiyun 			}
911*4882a593Smuzhiyun 		}
912*4882a593Smuzhiyun 		i++;
913*4882a593Smuzhiyun 		start += 2;
914*4882a593Smuzhiyun 		if (i >= vc->vc_size_row)
915*4882a593Smuzhiyun 			break;
916*4882a593Smuzhiyun 	}
917*4882a593Smuzhiyun 
918*4882a593Smuzhiyun 	for (--i; i >= 0; i--)
919*4882a593Smuzhiyun 		if (sentbuf[bn][i] != SPACE)
920*4882a593Smuzhiyun 			break;
921*4882a593Smuzhiyun 
922*4882a593Smuzhiyun 	if (i < 1)
923*4882a593Smuzhiyun 		return -1;
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun 	sentbuf[bn][++i] = SPACE;
926*4882a593Smuzhiyun 	sentbuf[bn][++i] = '\0';
927*4882a593Smuzhiyun 
928*4882a593Smuzhiyun 	sentbufend[bn] = &sentbuf[bn][i];
929*4882a593Smuzhiyun 	return numsentences[bn];
930*4882a593Smuzhiyun }
931*4882a593Smuzhiyun 
say_screen_from_to(struct vc_data * vc,u_long from,u_long to)932*4882a593Smuzhiyun static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
933*4882a593Smuzhiyun {
934*4882a593Smuzhiyun 	u_long start = vc->vc_origin, end;
935*4882a593Smuzhiyun 
936*4882a593Smuzhiyun 	if (from > 0)
937*4882a593Smuzhiyun 		start += from * vc->vc_size_row;
938*4882a593Smuzhiyun 	if (to > vc->vc_rows)
939*4882a593Smuzhiyun 		to = vc->vc_rows;
940*4882a593Smuzhiyun 	end = vc->vc_origin + (to * vc->vc_size_row);
941*4882a593Smuzhiyun 	for (from = start; from < end; from = to) {
942*4882a593Smuzhiyun 		to = from + vc->vc_size_row;
943*4882a593Smuzhiyun 		say_from_to(vc, from, to, 1);
944*4882a593Smuzhiyun 	}
945*4882a593Smuzhiyun }
946*4882a593Smuzhiyun 
say_screen(struct vc_data * vc)947*4882a593Smuzhiyun static void say_screen(struct vc_data *vc)
948*4882a593Smuzhiyun {
949*4882a593Smuzhiyun 	say_screen_from_to(vc, 0, vc->vc_rows);
950*4882a593Smuzhiyun }
951*4882a593Smuzhiyun 
speakup_win_say(struct vc_data * vc)952*4882a593Smuzhiyun static void speakup_win_say(struct vc_data *vc)
953*4882a593Smuzhiyun {
954*4882a593Smuzhiyun 	u_long start, end, from, to;
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 	if (win_start < 2) {
957*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
958*4882a593Smuzhiyun 		return;
959*4882a593Smuzhiyun 	}
960*4882a593Smuzhiyun 	start = vc->vc_origin + (win_top * vc->vc_size_row);
961*4882a593Smuzhiyun 	end = vc->vc_origin + (win_bottom * vc->vc_size_row);
962*4882a593Smuzhiyun 	while (start <= end) {
963*4882a593Smuzhiyun 		from = start + (win_left * 2);
964*4882a593Smuzhiyun 		to = start + (win_right * 2);
965*4882a593Smuzhiyun 		say_from_to(vc, from, to, 1);
966*4882a593Smuzhiyun 		start += vc->vc_size_row;
967*4882a593Smuzhiyun 	}
968*4882a593Smuzhiyun }
969*4882a593Smuzhiyun 
top_edge(struct vc_data * vc)970*4882a593Smuzhiyun static void top_edge(struct vc_data *vc)
971*4882a593Smuzhiyun {
972*4882a593Smuzhiyun 	spk_parked |= 0x01;
973*4882a593Smuzhiyun 	spk_pos = vc->vc_origin + 2 * spk_x;
974*4882a593Smuzhiyun 	spk_y = 0;
975*4882a593Smuzhiyun 	say_line(vc);
976*4882a593Smuzhiyun }
977*4882a593Smuzhiyun 
bottom_edge(struct vc_data * vc)978*4882a593Smuzhiyun static void bottom_edge(struct vc_data *vc)
979*4882a593Smuzhiyun {
980*4882a593Smuzhiyun 	spk_parked |= 0x01;
981*4882a593Smuzhiyun 	spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
982*4882a593Smuzhiyun 	spk_y = vc->vc_rows - 1;
983*4882a593Smuzhiyun 	say_line(vc);
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun 
left_edge(struct vc_data * vc)986*4882a593Smuzhiyun static void left_edge(struct vc_data *vc)
987*4882a593Smuzhiyun {
988*4882a593Smuzhiyun 	spk_parked |= 0x01;
989*4882a593Smuzhiyun 	spk_pos -= spk_x * 2;
990*4882a593Smuzhiyun 	spk_x = 0;
991*4882a593Smuzhiyun 	say_char(vc);
992*4882a593Smuzhiyun }
993*4882a593Smuzhiyun 
right_edge(struct vc_data * vc)994*4882a593Smuzhiyun static void right_edge(struct vc_data *vc)
995*4882a593Smuzhiyun {
996*4882a593Smuzhiyun 	spk_parked |= 0x01;
997*4882a593Smuzhiyun 	spk_pos += (vc->vc_cols - spk_x - 1) * 2;
998*4882a593Smuzhiyun 	spk_x = vc->vc_cols - 1;
999*4882a593Smuzhiyun 	say_char(vc);
1000*4882a593Smuzhiyun }
1001*4882a593Smuzhiyun 
say_first_char(struct vc_data * vc)1002*4882a593Smuzhiyun static void say_first_char(struct vc_data *vc)
1003*4882a593Smuzhiyun {
1004*4882a593Smuzhiyun 	int i, len = get_line(vc);
1005*4882a593Smuzhiyun 	u16 ch;
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun 	spk_parked |= 0x01;
1008*4882a593Smuzhiyun 	if (len == 0) {
1009*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1010*4882a593Smuzhiyun 		return;
1011*4882a593Smuzhiyun 	}
1012*4882a593Smuzhiyun 	for (i = 0; i < len; i++)
1013*4882a593Smuzhiyun 		if (buf[i] != SPACE)
1014*4882a593Smuzhiyun 			break;
1015*4882a593Smuzhiyun 	ch = buf[i];
1016*4882a593Smuzhiyun 	spk_pos -= (spk_x - i) * 2;
1017*4882a593Smuzhiyun 	spk_x = i;
1018*4882a593Smuzhiyun 	synth_printf("%d, ", ++i);
1019*4882a593Smuzhiyun 	speak_char(ch);
1020*4882a593Smuzhiyun }
1021*4882a593Smuzhiyun 
say_last_char(struct vc_data * vc)1022*4882a593Smuzhiyun static void say_last_char(struct vc_data *vc)
1023*4882a593Smuzhiyun {
1024*4882a593Smuzhiyun 	int len = get_line(vc);
1025*4882a593Smuzhiyun 	u16 ch;
1026*4882a593Smuzhiyun 
1027*4882a593Smuzhiyun 	spk_parked |= 0x01;
1028*4882a593Smuzhiyun 	if (len == 0) {
1029*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1030*4882a593Smuzhiyun 		return;
1031*4882a593Smuzhiyun 	}
1032*4882a593Smuzhiyun 	ch = buf[--len];
1033*4882a593Smuzhiyun 	spk_pos -= (spk_x - len) * 2;
1034*4882a593Smuzhiyun 	spk_x = len;
1035*4882a593Smuzhiyun 	synth_printf("%d, ", ++len);
1036*4882a593Smuzhiyun 	speak_char(ch);
1037*4882a593Smuzhiyun }
1038*4882a593Smuzhiyun 
say_position(struct vc_data * vc)1039*4882a593Smuzhiyun static void say_position(struct vc_data *vc)
1040*4882a593Smuzhiyun {
1041*4882a593Smuzhiyun 	synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
1042*4882a593Smuzhiyun 		     vc->vc_num + 1);
1043*4882a593Smuzhiyun 	synth_printf("\n");
1044*4882a593Smuzhiyun }
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun /* Added by brianb */
say_char_num(struct vc_data * vc)1047*4882a593Smuzhiyun static void say_char_num(struct vc_data *vc)
1048*4882a593Smuzhiyun {
1049*4882a593Smuzhiyun 	u_char tmp;
1050*4882a593Smuzhiyun 	u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
1051*4882a593Smuzhiyun 
1052*4882a593Smuzhiyun 	synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
1053*4882a593Smuzhiyun }
1054*4882a593Smuzhiyun 
1055*4882a593Smuzhiyun /* these are stub functions to keep keyboard.c happy. */
1056*4882a593Smuzhiyun 
say_from_top(struct vc_data * vc)1057*4882a593Smuzhiyun static void say_from_top(struct vc_data *vc)
1058*4882a593Smuzhiyun {
1059*4882a593Smuzhiyun 	say_screen_from_to(vc, 0, spk_y);
1060*4882a593Smuzhiyun }
1061*4882a593Smuzhiyun 
say_to_bottom(struct vc_data * vc)1062*4882a593Smuzhiyun static void say_to_bottom(struct vc_data *vc)
1063*4882a593Smuzhiyun {
1064*4882a593Smuzhiyun 	say_screen_from_to(vc, spk_y, vc->vc_rows);
1065*4882a593Smuzhiyun }
1066*4882a593Smuzhiyun 
say_from_left(struct vc_data * vc)1067*4882a593Smuzhiyun static void say_from_left(struct vc_data *vc)
1068*4882a593Smuzhiyun {
1069*4882a593Smuzhiyun 	say_line_from_to(vc, 0, spk_x, 1);
1070*4882a593Smuzhiyun }
1071*4882a593Smuzhiyun 
say_to_right(struct vc_data * vc)1072*4882a593Smuzhiyun static void say_to_right(struct vc_data *vc)
1073*4882a593Smuzhiyun {
1074*4882a593Smuzhiyun 	say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1075*4882a593Smuzhiyun }
1076*4882a593Smuzhiyun 
1077*4882a593Smuzhiyun /* end of stub functions. */
1078*4882a593Smuzhiyun 
spkup_write(const u16 * in_buf,int count)1079*4882a593Smuzhiyun static void spkup_write(const u16 *in_buf, int count)
1080*4882a593Smuzhiyun {
1081*4882a593Smuzhiyun 	static int rep_count;
1082*4882a593Smuzhiyun 	static u16 ch = '\0', old_ch = '\0';
1083*4882a593Smuzhiyun 	static u_short char_type, last_type;
1084*4882a593Smuzhiyun 	int in_count = count;
1085*4882a593Smuzhiyun 
1086*4882a593Smuzhiyun 	spk_keydown = 0;
1087*4882a593Smuzhiyun 	while (count--) {
1088*4882a593Smuzhiyun 		if (cursor_track == read_all_mode) {
1089*4882a593Smuzhiyun 			/* Insert Sentence Index */
1090*4882a593Smuzhiyun 			if ((in_buf == sentmarks[bn][currsentence]) &&
1091*4882a593Smuzhiyun 			    (currsentence <= numsentences[bn]))
1092*4882a593Smuzhiyun 				synth_insert_next_index(currsentence++);
1093*4882a593Smuzhiyun 		}
1094*4882a593Smuzhiyun 		ch = *in_buf++;
1095*4882a593Smuzhiyun 		if (ch < 0x100)
1096*4882a593Smuzhiyun 			char_type = spk_chartab[ch];
1097*4882a593Smuzhiyun 		else
1098*4882a593Smuzhiyun 			char_type = ALPHA;
1099*4882a593Smuzhiyun 		if (ch == old_ch && !(char_type & B_NUM)) {
1100*4882a593Smuzhiyun 			if (++rep_count > 2)
1101*4882a593Smuzhiyun 				continue;
1102*4882a593Smuzhiyun 		} else {
1103*4882a593Smuzhiyun 			if ((last_type & CH_RPT) && rep_count > 2) {
1104*4882a593Smuzhiyun 				synth_printf(" ");
1105*4882a593Smuzhiyun 				synth_printf(spk_msg_get(MSG_REPEAT_DESC),
1106*4882a593Smuzhiyun 					     ++rep_count);
1107*4882a593Smuzhiyun 				synth_printf(" ");
1108*4882a593Smuzhiyun 			}
1109*4882a593Smuzhiyun 			rep_count = 0;
1110*4882a593Smuzhiyun 		}
1111*4882a593Smuzhiyun 		if (ch == spk_lastkey) {
1112*4882a593Smuzhiyun 			rep_count = 0;
1113*4882a593Smuzhiyun 			if (spk_key_echo == 1 && ch >= MINECHOCHAR)
1114*4882a593Smuzhiyun 				speak_char(ch);
1115*4882a593Smuzhiyun 		} else if (char_type & B_ALPHA) {
1116*4882a593Smuzhiyun 			if ((synth_flags & SF_DEC) && (last_type & PUNC))
1117*4882a593Smuzhiyun 				synth_buffer_add(SPACE);
1118*4882a593Smuzhiyun 			synth_putwc_s(ch);
1119*4882a593Smuzhiyun 		} else if (char_type & B_NUM) {
1120*4882a593Smuzhiyun 			rep_count = 0;
1121*4882a593Smuzhiyun 			synth_putwc_s(ch);
1122*4882a593Smuzhiyun 		} else if (char_type & spk_punc_mask) {
1123*4882a593Smuzhiyun 			speak_char(ch);
1124*4882a593Smuzhiyun 			char_type &= ~PUNC;	/* for dec nospell processing */
1125*4882a593Smuzhiyun 		} else if (char_type & SYNTH_OK) {
1126*4882a593Smuzhiyun 			/* these are usually puncts like . and , which synth
1127*4882a593Smuzhiyun 			 * needs for expression.
1128*4882a593Smuzhiyun 			 * suppress multiple to get rid of long pauses and
1129*4882a593Smuzhiyun 			 * clear repeat count
1130*4882a593Smuzhiyun 			 * so if someone has
1131*4882a593Smuzhiyun 			 * repeats on you don't get nothing repeated count
1132*4882a593Smuzhiyun 			 */
1133*4882a593Smuzhiyun 			if (ch != old_ch)
1134*4882a593Smuzhiyun 				synth_putwc_s(ch);
1135*4882a593Smuzhiyun 			else
1136*4882a593Smuzhiyun 				rep_count = 0;
1137*4882a593Smuzhiyun 		} else {
1138*4882a593Smuzhiyun /* send space and record position, if next is num overwrite space */
1139*4882a593Smuzhiyun 			if (old_ch != ch)
1140*4882a593Smuzhiyun 				synth_buffer_add(SPACE);
1141*4882a593Smuzhiyun 			else
1142*4882a593Smuzhiyun 				rep_count = 0;
1143*4882a593Smuzhiyun 		}
1144*4882a593Smuzhiyun 		old_ch = ch;
1145*4882a593Smuzhiyun 		last_type = char_type;
1146*4882a593Smuzhiyun 	}
1147*4882a593Smuzhiyun 	spk_lastkey = 0;
1148*4882a593Smuzhiyun 	if (in_count > 2 && rep_count > 2) {
1149*4882a593Smuzhiyun 		if (last_type & CH_RPT) {
1150*4882a593Smuzhiyun 			synth_printf(" ");
1151*4882a593Smuzhiyun 			synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1152*4882a593Smuzhiyun 				     ++rep_count);
1153*4882a593Smuzhiyun 			synth_printf(" ");
1154*4882a593Smuzhiyun 		}
1155*4882a593Smuzhiyun 		rep_count = 0;
1156*4882a593Smuzhiyun 	}
1157*4882a593Smuzhiyun }
1158*4882a593Smuzhiyun 
1159*4882a593Smuzhiyun static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1160*4882a593Smuzhiyun 
1161*4882a593Smuzhiyun static void read_all_doc(struct vc_data *vc);
1162*4882a593Smuzhiyun static void cursor_done(struct timer_list *unused);
1163*4882a593Smuzhiyun static DEFINE_TIMER(cursor_timer, cursor_done);
1164*4882a593Smuzhiyun 
do_handle_shift(struct vc_data * vc,u_char value,char up_flag)1165*4882a593Smuzhiyun static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1166*4882a593Smuzhiyun {
1167*4882a593Smuzhiyun 	unsigned long flags;
1168*4882a593Smuzhiyun 
1169*4882a593Smuzhiyun 	if (!synth || up_flag || spk_killed)
1170*4882a593Smuzhiyun 		return;
1171*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
1172*4882a593Smuzhiyun 	if (cursor_track == read_all_mode) {
1173*4882a593Smuzhiyun 		switch (value) {
1174*4882a593Smuzhiyun 		case KVAL(K_SHIFT):
1175*4882a593Smuzhiyun 			del_timer(&cursor_timer);
1176*4882a593Smuzhiyun 			spk_shut_up &= 0xfe;
1177*4882a593Smuzhiyun 			spk_do_flush();
1178*4882a593Smuzhiyun 			read_all_doc(vc);
1179*4882a593Smuzhiyun 			break;
1180*4882a593Smuzhiyun 		case KVAL(K_CTRL):
1181*4882a593Smuzhiyun 			del_timer(&cursor_timer);
1182*4882a593Smuzhiyun 			cursor_track = prev_cursor_track;
1183*4882a593Smuzhiyun 			spk_shut_up &= 0xfe;
1184*4882a593Smuzhiyun 			spk_do_flush();
1185*4882a593Smuzhiyun 			break;
1186*4882a593Smuzhiyun 		}
1187*4882a593Smuzhiyun 	} else {
1188*4882a593Smuzhiyun 		spk_shut_up &= 0xfe;
1189*4882a593Smuzhiyun 		spk_do_flush();
1190*4882a593Smuzhiyun 	}
1191*4882a593Smuzhiyun 	if (spk_say_ctrl && value < NUM_CTL_LABELS)
1192*4882a593Smuzhiyun 		synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
1193*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1194*4882a593Smuzhiyun }
1195*4882a593Smuzhiyun 
do_handle_latin(struct vc_data * vc,u_char value,char up_flag)1196*4882a593Smuzhiyun static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1197*4882a593Smuzhiyun {
1198*4882a593Smuzhiyun 	unsigned long flags;
1199*4882a593Smuzhiyun 
1200*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
1201*4882a593Smuzhiyun 	if (up_flag) {
1202*4882a593Smuzhiyun 		spk_lastkey = 0;
1203*4882a593Smuzhiyun 		spk_keydown = 0;
1204*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1205*4882a593Smuzhiyun 		return;
1206*4882a593Smuzhiyun 	}
1207*4882a593Smuzhiyun 	if (!synth || spk_killed) {
1208*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1209*4882a593Smuzhiyun 		return;
1210*4882a593Smuzhiyun 	}
1211*4882a593Smuzhiyun 	spk_shut_up &= 0xfe;
1212*4882a593Smuzhiyun 	spk_lastkey = value;
1213*4882a593Smuzhiyun 	spk_keydown++;
1214*4882a593Smuzhiyun 	spk_parked &= 0xfe;
1215*4882a593Smuzhiyun 	if (spk_key_echo == 2 && value >= MINECHOCHAR)
1216*4882a593Smuzhiyun 		speak_char(value);
1217*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1218*4882a593Smuzhiyun }
1219*4882a593Smuzhiyun 
spk_set_key_info(const u_char * key_info,u_char * k_buffer)1220*4882a593Smuzhiyun int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
1221*4882a593Smuzhiyun {
1222*4882a593Smuzhiyun 	int i = 0, states, key_data_len;
1223*4882a593Smuzhiyun 	const u_char *cp = key_info;
1224*4882a593Smuzhiyun 	u_char *cp1 = k_buffer;
1225*4882a593Smuzhiyun 	u_char ch, version, num_keys;
1226*4882a593Smuzhiyun 
1227*4882a593Smuzhiyun 	version = *cp++;
1228*4882a593Smuzhiyun 	if (version != KEY_MAP_VER) {
1229*4882a593Smuzhiyun 		pr_debug("version found %d should be %d\n",
1230*4882a593Smuzhiyun 			 version, KEY_MAP_VER);
1231*4882a593Smuzhiyun 		return -EINVAL;
1232*4882a593Smuzhiyun 	}
1233*4882a593Smuzhiyun 	num_keys = *cp;
1234*4882a593Smuzhiyun 	states = (int)cp[1];
1235*4882a593Smuzhiyun 	key_data_len = (states + 1) * (num_keys + 1);
1236*4882a593Smuzhiyun 	if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
1237*4882a593Smuzhiyun 		pr_debug("too many key_infos (%d over %u)\n",
1238*4882a593Smuzhiyun 			 key_data_len + SHIFT_TBL_SIZE + 4,
1239*4882a593Smuzhiyun 			 (unsigned int)(sizeof(spk_key_buf)));
1240*4882a593Smuzhiyun 		return -EINVAL;
1241*4882a593Smuzhiyun 	}
1242*4882a593Smuzhiyun 	memset(k_buffer, 0, SHIFT_TBL_SIZE);
1243*4882a593Smuzhiyun 	memset(spk_our_keys, 0, sizeof(spk_our_keys));
1244*4882a593Smuzhiyun 	spk_shift_table = k_buffer;
1245*4882a593Smuzhiyun 	spk_our_keys[0] = spk_shift_table;
1246*4882a593Smuzhiyun 	cp1 += SHIFT_TBL_SIZE;
1247*4882a593Smuzhiyun 	memcpy(cp1, cp, key_data_len + 3);
1248*4882a593Smuzhiyun 	/* get num_keys, states and data */
1249*4882a593Smuzhiyun 	cp1 += 2;		/* now pointing at shift states */
1250*4882a593Smuzhiyun 	for (i = 1; i <= states; i++) {
1251*4882a593Smuzhiyun 		ch = *cp1++;
1252*4882a593Smuzhiyun 		if (ch >= SHIFT_TBL_SIZE) {
1253*4882a593Smuzhiyun 			pr_debug("(%d) not valid shift state (max_allowed = %d)\n",
1254*4882a593Smuzhiyun 				 ch, SHIFT_TBL_SIZE);
1255*4882a593Smuzhiyun 			return -EINVAL;
1256*4882a593Smuzhiyun 		}
1257*4882a593Smuzhiyun 		spk_shift_table[ch] = i;
1258*4882a593Smuzhiyun 	}
1259*4882a593Smuzhiyun 	keymap_flags = *cp1++;
1260*4882a593Smuzhiyun 	while ((ch = *cp1)) {
1261*4882a593Smuzhiyun 		if (ch >= MAX_KEY) {
1262*4882a593Smuzhiyun 			pr_debug("(%d), not valid key, (max_allowed = %d)\n",
1263*4882a593Smuzhiyun 				 ch, MAX_KEY);
1264*4882a593Smuzhiyun 			return -EINVAL;
1265*4882a593Smuzhiyun 		}
1266*4882a593Smuzhiyun 		spk_our_keys[ch] = cp1;
1267*4882a593Smuzhiyun 		cp1 += states + 1;
1268*4882a593Smuzhiyun 	}
1269*4882a593Smuzhiyun 	return 0;
1270*4882a593Smuzhiyun }
1271*4882a593Smuzhiyun 
1272*4882a593Smuzhiyun static struct var_t spk_vars[] = {
1273*4882a593Smuzhiyun 	/* bell must be first to set high limit */
1274*4882a593Smuzhiyun 	{BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
1275*4882a593Smuzhiyun 	{SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
1276*4882a593Smuzhiyun 	{ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
1277*4882a593Smuzhiyun 	{BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
1278*4882a593Smuzhiyun 	{BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
1279*4882a593Smuzhiyun 	{PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1280*4882a593Smuzhiyun 	{READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1281*4882a593Smuzhiyun 	{CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
1282*4882a593Smuzhiyun 	{SAY_CONTROL, TOGGLE_0},
1283*4882a593Smuzhiyun 	{SAY_WORD_CTL, TOGGLE_0},
1284*4882a593Smuzhiyun 	{NO_INTERRUPT, TOGGLE_0},
1285*4882a593Smuzhiyun 	{KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
1286*4882a593Smuzhiyun 	V_LAST_VAR
1287*4882a593Smuzhiyun };
1288*4882a593Smuzhiyun 
toggle_cursoring(struct vc_data * vc)1289*4882a593Smuzhiyun static void toggle_cursoring(struct vc_data *vc)
1290*4882a593Smuzhiyun {
1291*4882a593Smuzhiyun 	if (cursor_track == read_all_mode)
1292*4882a593Smuzhiyun 		cursor_track = prev_cursor_track;
1293*4882a593Smuzhiyun 	if (++cursor_track >= CT_Max)
1294*4882a593Smuzhiyun 		cursor_track = 0;
1295*4882a593Smuzhiyun 	synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
1296*4882a593Smuzhiyun }
1297*4882a593Smuzhiyun 
spk_reset_default_chars(void)1298*4882a593Smuzhiyun void spk_reset_default_chars(void)
1299*4882a593Smuzhiyun {
1300*4882a593Smuzhiyun 	int i;
1301*4882a593Smuzhiyun 
1302*4882a593Smuzhiyun 	/* First, free any non-default */
1303*4882a593Smuzhiyun 	for (i = 0; i < 256; i++) {
1304*4882a593Smuzhiyun 		if (spk_characters[i] &&
1305*4882a593Smuzhiyun 		    (spk_characters[i] != spk_default_chars[i]))
1306*4882a593Smuzhiyun 			kfree(spk_characters[i]);
1307*4882a593Smuzhiyun 	}
1308*4882a593Smuzhiyun 
1309*4882a593Smuzhiyun 	memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
1310*4882a593Smuzhiyun }
1311*4882a593Smuzhiyun 
spk_reset_default_chartab(void)1312*4882a593Smuzhiyun void spk_reset_default_chartab(void)
1313*4882a593Smuzhiyun {
1314*4882a593Smuzhiyun 	memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1315*4882a593Smuzhiyun }
1316*4882a593Smuzhiyun 
1317*4882a593Smuzhiyun static const struct st_bits_data *pb_edit;
1318*4882a593Smuzhiyun 
edit_bits(struct vc_data * vc,u_char type,u_char ch,u_short key)1319*4882a593Smuzhiyun static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1320*4882a593Smuzhiyun {
1321*4882a593Smuzhiyun 	short mask = pb_edit->mask, ch_type = spk_chartab[ch];
1322*4882a593Smuzhiyun 
1323*4882a593Smuzhiyun 	if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
1324*4882a593Smuzhiyun 		return -1;
1325*4882a593Smuzhiyun 	if (ch == SPACE) {
1326*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1327*4882a593Smuzhiyun 		spk_special_handler = NULL;
1328*4882a593Smuzhiyun 		return 1;
1329*4882a593Smuzhiyun 	}
1330*4882a593Smuzhiyun 	if (mask < PUNC && !(ch_type & PUNC))
1331*4882a593Smuzhiyun 		return -1;
1332*4882a593Smuzhiyun 	spk_chartab[ch] ^= mask;
1333*4882a593Smuzhiyun 	speak_char(ch);
1334*4882a593Smuzhiyun 	synth_printf(" %s\n",
1335*4882a593Smuzhiyun 		     (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1336*4882a593Smuzhiyun 		     spk_msg_get(MSG_OFF));
1337*4882a593Smuzhiyun 	return 1;
1338*4882a593Smuzhiyun }
1339*4882a593Smuzhiyun 
1340*4882a593Smuzhiyun /* Allocation concurrency is protected by the console semaphore */
speakup_allocate(struct vc_data * vc,gfp_t gfp_flags)1341*4882a593Smuzhiyun static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)
1342*4882a593Smuzhiyun {
1343*4882a593Smuzhiyun 	int vc_num;
1344*4882a593Smuzhiyun 
1345*4882a593Smuzhiyun 	vc_num = vc->vc_num;
1346*4882a593Smuzhiyun 	if (!speakup_console[vc_num]) {
1347*4882a593Smuzhiyun 		speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
1348*4882a593Smuzhiyun 						  gfp_flags);
1349*4882a593Smuzhiyun 		if (!speakup_console[vc_num])
1350*4882a593Smuzhiyun 			return -ENOMEM;
1351*4882a593Smuzhiyun 		speakup_date(vc);
1352*4882a593Smuzhiyun 	} else if (!spk_parked) {
1353*4882a593Smuzhiyun 		speakup_date(vc);
1354*4882a593Smuzhiyun 	}
1355*4882a593Smuzhiyun 
1356*4882a593Smuzhiyun 	return 0;
1357*4882a593Smuzhiyun }
1358*4882a593Smuzhiyun 
speakup_deallocate(struct vc_data * vc)1359*4882a593Smuzhiyun static void speakup_deallocate(struct vc_data *vc)
1360*4882a593Smuzhiyun {
1361*4882a593Smuzhiyun 	int vc_num;
1362*4882a593Smuzhiyun 
1363*4882a593Smuzhiyun 	vc_num = vc->vc_num;
1364*4882a593Smuzhiyun 	kfree(speakup_console[vc_num]);
1365*4882a593Smuzhiyun 	speakup_console[vc_num] = NULL;
1366*4882a593Smuzhiyun }
1367*4882a593Smuzhiyun 
1368*4882a593Smuzhiyun static u_char is_cursor;
1369*4882a593Smuzhiyun static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1370*4882a593Smuzhiyun static int cursor_con;
1371*4882a593Smuzhiyun 
1372*4882a593Smuzhiyun static void reset_highlight_buffers(struct vc_data *);
1373*4882a593Smuzhiyun 
1374*4882a593Smuzhiyun static int read_all_key;
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun static int in_keyboard_notifier;
1377*4882a593Smuzhiyun 
1378*4882a593Smuzhiyun static void start_read_all_timer(struct vc_data *vc, int command);
1379*4882a593Smuzhiyun 
1380*4882a593Smuzhiyun enum {
1381*4882a593Smuzhiyun 	RA_NOTHING,
1382*4882a593Smuzhiyun 	RA_NEXT_SENT,
1383*4882a593Smuzhiyun 	RA_PREV_LINE,
1384*4882a593Smuzhiyun 	RA_NEXT_LINE,
1385*4882a593Smuzhiyun 	RA_PREV_SENT,
1386*4882a593Smuzhiyun 	RA_DOWN_ARROW,
1387*4882a593Smuzhiyun 	RA_TIMER,
1388*4882a593Smuzhiyun 	RA_FIND_NEXT_SENT,
1389*4882a593Smuzhiyun 	RA_FIND_PREV_SENT,
1390*4882a593Smuzhiyun };
1391*4882a593Smuzhiyun 
kbd_fakekey2(struct vc_data * vc,int command)1392*4882a593Smuzhiyun static void kbd_fakekey2(struct vc_data *vc, int command)
1393*4882a593Smuzhiyun {
1394*4882a593Smuzhiyun 	del_timer(&cursor_timer);
1395*4882a593Smuzhiyun 	speakup_fake_down_arrow();
1396*4882a593Smuzhiyun 	start_read_all_timer(vc, command);
1397*4882a593Smuzhiyun }
1398*4882a593Smuzhiyun 
read_all_doc(struct vc_data * vc)1399*4882a593Smuzhiyun static void read_all_doc(struct vc_data *vc)
1400*4882a593Smuzhiyun {
1401*4882a593Smuzhiyun 	if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
1402*4882a593Smuzhiyun 		return;
1403*4882a593Smuzhiyun 	if (!synth_supports_indexing())
1404*4882a593Smuzhiyun 		return;
1405*4882a593Smuzhiyun 	if (cursor_track != read_all_mode)
1406*4882a593Smuzhiyun 		prev_cursor_track = cursor_track;
1407*4882a593Smuzhiyun 	cursor_track = read_all_mode;
1408*4882a593Smuzhiyun 	spk_reset_index_count(0);
1409*4882a593Smuzhiyun 	if (get_sentence_buf(vc, 0) == -1) {
1410*4882a593Smuzhiyun 		del_timer(&cursor_timer);
1411*4882a593Smuzhiyun 		if (!in_keyboard_notifier)
1412*4882a593Smuzhiyun 			speakup_fake_down_arrow();
1413*4882a593Smuzhiyun 		start_read_all_timer(vc, RA_DOWN_ARROW);
1414*4882a593Smuzhiyun 	} else {
1415*4882a593Smuzhiyun 		say_sentence_num(0, 0);
1416*4882a593Smuzhiyun 		synth_insert_next_index(0);
1417*4882a593Smuzhiyun 		start_read_all_timer(vc, RA_TIMER);
1418*4882a593Smuzhiyun 	}
1419*4882a593Smuzhiyun }
1420*4882a593Smuzhiyun 
stop_read_all(struct vc_data * vc)1421*4882a593Smuzhiyun static void stop_read_all(struct vc_data *vc)
1422*4882a593Smuzhiyun {
1423*4882a593Smuzhiyun 	del_timer(&cursor_timer);
1424*4882a593Smuzhiyun 	cursor_track = prev_cursor_track;
1425*4882a593Smuzhiyun 	spk_shut_up &= 0xfe;
1426*4882a593Smuzhiyun 	spk_do_flush();
1427*4882a593Smuzhiyun }
1428*4882a593Smuzhiyun 
start_read_all_timer(struct vc_data * vc,int command)1429*4882a593Smuzhiyun static void start_read_all_timer(struct vc_data *vc, int command)
1430*4882a593Smuzhiyun {
1431*4882a593Smuzhiyun 	struct var_t *cursor_timeout;
1432*4882a593Smuzhiyun 
1433*4882a593Smuzhiyun 	cursor_con = vc->vc_num;
1434*4882a593Smuzhiyun 	read_all_key = command;
1435*4882a593Smuzhiyun 	cursor_timeout = spk_get_var(CURSOR_TIME);
1436*4882a593Smuzhiyun 	mod_timer(&cursor_timer,
1437*4882a593Smuzhiyun 		  jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1438*4882a593Smuzhiyun }
1439*4882a593Smuzhiyun 
handle_cursor_read_all(struct vc_data * vc,int command)1440*4882a593Smuzhiyun static void handle_cursor_read_all(struct vc_data *vc, int command)
1441*4882a593Smuzhiyun {
1442*4882a593Smuzhiyun 	int indcount, sentcount, rv, sn;
1443*4882a593Smuzhiyun 
1444*4882a593Smuzhiyun 	switch (command) {
1445*4882a593Smuzhiyun 	case RA_NEXT_SENT:
1446*4882a593Smuzhiyun 		/* Get Current Sentence */
1447*4882a593Smuzhiyun 		spk_get_index_count(&indcount, &sentcount);
1448*4882a593Smuzhiyun 		/*printk("%d %d  ", indcount, sentcount); */
1449*4882a593Smuzhiyun 		spk_reset_index_count(sentcount + 1);
1450*4882a593Smuzhiyun 		if (indcount == 1) {
1451*4882a593Smuzhiyun 			if (!say_sentence_num(sentcount + 1, 0)) {
1452*4882a593Smuzhiyun 				kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1453*4882a593Smuzhiyun 				return;
1454*4882a593Smuzhiyun 			}
1455*4882a593Smuzhiyun 			synth_insert_next_index(0);
1456*4882a593Smuzhiyun 		} else {
1457*4882a593Smuzhiyun 			sn = 0;
1458*4882a593Smuzhiyun 			if (!say_sentence_num(sentcount + 1, 1)) {
1459*4882a593Smuzhiyun 				sn = 1;
1460*4882a593Smuzhiyun 				spk_reset_index_count(sn);
1461*4882a593Smuzhiyun 			} else {
1462*4882a593Smuzhiyun 				synth_insert_next_index(0);
1463*4882a593Smuzhiyun 			}
1464*4882a593Smuzhiyun 			if (!say_sentence_num(sn, 0)) {
1465*4882a593Smuzhiyun 				kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1466*4882a593Smuzhiyun 				return;
1467*4882a593Smuzhiyun 			}
1468*4882a593Smuzhiyun 			synth_insert_next_index(0);
1469*4882a593Smuzhiyun 		}
1470*4882a593Smuzhiyun 		start_read_all_timer(vc, RA_TIMER);
1471*4882a593Smuzhiyun 		break;
1472*4882a593Smuzhiyun 	case RA_PREV_SENT:
1473*4882a593Smuzhiyun 		break;
1474*4882a593Smuzhiyun 	case RA_NEXT_LINE:
1475*4882a593Smuzhiyun 		read_all_doc(vc);
1476*4882a593Smuzhiyun 		break;
1477*4882a593Smuzhiyun 	case RA_PREV_LINE:
1478*4882a593Smuzhiyun 		break;
1479*4882a593Smuzhiyun 	case RA_DOWN_ARROW:
1480*4882a593Smuzhiyun 		if (get_sentence_buf(vc, 0) == -1) {
1481*4882a593Smuzhiyun 			kbd_fakekey2(vc, RA_DOWN_ARROW);
1482*4882a593Smuzhiyun 		} else {
1483*4882a593Smuzhiyun 			say_sentence_num(0, 0);
1484*4882a593Smuzhiyun 			synth_insert_next_index(0);
1485*4882a593Smuzhiyun 			start_read_all_timer(vc, RA_TIMER);
1486*4882a593Smuzhiyun 		}
1487*4882a593Smuzhiyun 		break;
1488*4882a593Smuzhiyun 	case RA_FIND_NEXT_SENT:
1489*4882a593Smuzhiyun 		rv = get_sentence_buf(vc, 0);
1490*4882a593Smuzhiyun 		if (rv == -1)
1491*4882a593Smuzhiyun 			read_all_doc(vc);
1492*4882a593Smuzhiyun 		if (rv == 0) {
1493*4882a593Smuzhiyun 			kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1494*4882a593Smuzhiyun 		} else {
1495*4882a593Smuzhiyun 			say_sentence_num(1, 0);
1496*4882a593Smuzhiyun 			synth_insert_next_index(0);
1497*4882a593Smuzhiyun 			start_read_all_timer(vc, RA_TIMER);
1498*4882a593Smuzhiyun 		}
1499*4882a593Smuzhiyun 		break;
1500*4882a593Smuzhiyun 	case RA_FIND_PREV_SENT:
1501*4882a593Smuzhiyun 		break;
1502*4882a593Smuzhiyun 	case RA_TIMER:
1503*4882a593Smuzhiyun 		spk_get_index_count(&indcount, &sentcount);
1504*4882a593Smuzhiyun 		if (indcount < 2)
1505*4882a593Smuzhiyun 			kbd_fakekey2(vc, RA_DOWN_ARROW);
1506*4882a593Smuzhiyun 		else
1507*4882a593Smuzhiyun 			start_read_all_timer(vc, RA_TIMER);
1508*4882a593Smuzhiyun 		break;
1509*4882a593Smuzhiyun 	}
1510*4882a593Smuzhiyun }
1511*4882a593Smuzhiyun 
pre_handle_cursor(struct vc_data * vc,u_char value,char up_flag)1512*4882a593Smuzhiyun static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1513*4882a593Smuzhiyun {
1514*4882a593Smuzhiyun 	unsigned long flags;
1515*4882a593Smuzhiyun 
1516*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
1517*4882a593Smuzhiyun 	if (cursor_track == read_all_mode) {
1518*4882a593Smuzhiyun 		spk_parked &= 0xfe;
1519*4882a593Smuzhiyun 		if (!synth || up_flag || spk_shut_up) {
1520*4882a593Smuzhiyun 			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1521*4882a593Smuzhiyun 			return NOTIFY_STOP;
1522*4882a593Smuzhiyun 		}
1523*4882a593Smuzhiyun 		del_timer(&cursor_timer);
1524*4882a593Smuzhiyun 		spk_shut_up &= 0xfe;
1525*4882a593Smuzhiyun 		spk_do_flush();
1526*4882a593Smuzhiyun 		start_read_all_timer(vc, value + 1);
1527*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1528*4882a593Smuzhiyun 		return NOTIFY_STOP;
1529*4882a593Smuzhiyun 	}
1530*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1531*4882a593Smuzhiyun 	return NOTIFY_OK;
1532*4882a593Smuzhiyun }
1533*4882a593Smuzhiyun 
do_handle_cursor(struct vc_data * vc,u_char value,char up_flag)1534*4882a593Smuzhiyun static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1535*4882a593Smuzhiyun {
1536*4882a593Smuzhiyun 	unsigned long flags;
1537*4882a593Smuzhiyun 	struct var_t *cursor_timeout;
1538*4882a593Smuzhiyun 
1539*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
1540*4882a593Smuzhiyun 	spk_parked &= 0xfe;
1541*4882a593Smuzhiyun 	if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
1542*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1543*4882a593Smuzhiyun 		return;
1544*4882a593Smuzhiyun 	}
1545*4882a593Smuzhiyun 	spk_shut_up &= 0xfe;
1546*4882a593Smuzhiyun 	if (spk_no_intr)
1547*4882a593Smuzhiyun 		spk_do_flush();
1548*4882a593Smuzhiyun /* the key press flushes if !no_inter but we want to flush on cursor
1549*4882a593Smuzhiyun  * moves regardless of no_inter state
1550*4882a593Smuzhiyun  */
1551*4882a593Smuzhiyun 	is_cursor = value + 1;
1552*4882a593Smuzhiyun 	old_cursor_pos = vc->vc_pos;
1553*4882a593Smuzhiyun 	old_cursor_x = vc->state.x;
1554*4882a593Smuzhiyun 	old_cursor_y = vc->state.y;
1555*4882a593Smuzhiyun 	speakup_console[vc->vc_num]->ht.cy = vc->state.y;
1556*4882a593Smuzhiyun 	cursor_con = vc->vc_num;
1557*4882a593Smuzhiyun 	if (cursor_track == CT_Highlight)
1558*4882a593Smuzhiyun 		reset_highlight_buffers(vc);
1559*4882a593Smuzhiyun 	cursor_timeout = spk_get_var(CURSOR_TIME);
1560*4882a593Smuzhiyun 	mod_timer(&cursor_timer,
1561*4882a593Smuzhiyun 		  jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1562*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1563*4882a593Smuzhiyun }
1564*4882a593Smuzhiyun 
update_color_buffer(struct vc_data * vc,const u16 * ic,int len)1565*4882a593Smuzhiyun static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
1566*4882a593Smuzhiyun {
1567*4882a593Smuzhiyun 	int i, bi, hi;
1568*4882a593Smuzhiyun 	int vc_num = vc->vc_num;
1569*4882a593Smuzhiyun 
1570*4882a593Smuzhiyun 	bi = (vc->vc_attr & 0x70) >> 4;
1571*4882a593Smuzhiyun 	hi = speakup_console[vc_num]->ht.highsize[bi];
1572*4882a593Smuzhiyun 
1573*4882a593Smuzhiyun 	i = 0;
1574*4882a593Smuzhiyun 	if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1575*4882a593Smuzhiyun 		speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1576*4882a593Smuzhiyun 		speakup_console[vc_num]->ht.rx[bi] = vc->state.x;
1577*4882a593Smuzhiyun 		speakup_console[vc_num]->ht.ry[bi] = vc->state.y;
1578*4882a593Smuzhiyun 	}
1579*4882a593Smuzhiyun 	while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1580*4882a593Smuzhiyun 		if (ic[i] > 32) {
1581*4882a593Smuzhiyun 			speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1582*4882a593Smuzhiyun 			hi++;
1583*4882a593Smuzhiyun 		} else if ((ic[i] == 32) && (hi != 0)) {
1584*4882a593Smuzhiyun 			if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1585*4882a593Smuzhiyun 			    32) {
1586*4882a593Smuzhiyun 				speakup_console[vc_num]->ht.highbuf[bi][hi] =
1587*4882a593Smuzhiyun 				    ic[i];
1588*4882a593Smuzhiyun 				hi++;
1589*4882a593Smuzhiyun 			}
1590*4882a593Smuzhiyun 		}
1591*4882a593Smuzhiyun 		i++;
1592*4882a593Smuzhiyun 	}
1593*4882a593Smuzhiyun 	speakup_console[vc_num]->ht.highsize[bi] = hi;
1594*4882a593Smuzhiyun }
1595*4882a593Smuzhiyun 
reset_highlight_buffers(struct vc_data * vc)1596*4882a593Smuzhiyun static void reset_highlight_buffers(struct vc_data *vc)
1597*4882a593Smuzhiyun {
1598*4882a593Smuzhiyun 	int i;
1599*4882a593Smuzhiyun 	int vc_num = vc->vc_num;
1600*4882a593Smuzhiyun 
1601*4882a593Smuzhiyun 	for (i = 0; i < 8; i++)
1602*4882a593Smuzhiyun 		speakup_console[vc_num]->ht.highsize[i] = 0;
1603*4882a593Smuzhiyun }
1604*4882a593Smuzhiyun 
count_highlight_color(struct vc_data * vc)1605*4882a593Smuzhiyun static int count_highlight_color(struct vc_data *vc)
1606*4882a593Smuzhiyun {
1607*4882a593Smuzhiyun 	int i, bg;
1608*4882a593Smuzhiyun 	int cc;
1609*4882a593Smuzhiyun 	int vc_num = vc->vc_num;
1610*4882a593Smuzhiyun 	u16 ch;
1611*4882a593Smuzhiyun 	u16 *start = (u16 *)vc->vc_origin;
1612*4882a593Smuzhiyun 
1613*4882a593Smuzhiyun 	for (i = 0; i < 8; i++)
1614*4882a593Smuzhiyun 		speakup_console[vc_num]->ht.bgcount[i] = 0;
1615*4882a593Smuzhiyun 
1616*4882a593Smuzhiyun 	for (i = 0; i < vc->vc_rows; i++) {
1617*4882a593Smuzhiyun 		u16 *end = start + vc->vc_cols * 2;
1618*4882a593Smuzhiyun 		u16 *ptr;
1619*4882a593Smuzhiyun 
1620*4882a593Smuzhiyun 		for (ptr = start; ptr < end; ptr++) {
1621*4882a593Smuzhiyun 			ch = get_attributes(vc, ptr);
1622*4882a593Smuzhiyun 			bg = (ch & 0x70) >> 4;
1623*4882a593Smuzhiyun 			speakup_console[vc_num]->ht.bgcount[bg]++;
1624*4882a593Smuzhiyun 		}
1625*4882a593Smuzhiyun 		start += vc->vc_size_row;
1626*4882a593Smuzhiyun 	}
1627*4882a593Smuzhiyun 
1628*4882a593Smuzhiyun 	cc = 0;
1629*4882a593Smuzhiyun 	for (i = 0; i < 8; i++)
1630*4882a593Smuzhiyun 		if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1631*4882a593Smuzhiyun 			cc++;
1632*4882a593Smuzhiyun 	return cc;
1633*4882a593Smuzhiyun }
1634*4882a593Smuzhiyun 
get_highlight_color(struct vc_data * vc)1635*4882a593Smuzhiyun static int get_highlight_color(struct vc_data *vc)
1636*4882a593Smuzhiyun {
1637*4882a593Smuzhiyun 	int i, j;
1638*4882a593Smuzhiyun 	unsigned int cptr[8];
1639*4882a593Smuzhiyun 	int vc_num = vc->vc_num;
1640*4882a593Smuzhiyun 
1641*4882a593Smuzhiyun 	for (i = 0; i < 8; i++)
1642*4882a593Smuzhiyun 		cptr[i] = i;
1643*4882a593Smuzhiyun 
1644*4882a593Smuzhiyun 	for (i = 0; i < 7; i++)
1645*4882a593Smuzhiyun 		for (j = i + 1; j < 8; j++)
1646*4882a593Smuzhiyun 			if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
1647*4882a593Smuzhiyun 			    speakup_console[vc_num]->ht.bgcount[cptr[j]])
1648*4882a593Smuzhiyun 				swap(cptr[i], cptr[j]);
1649*4882a593Smuzhiyun 
1650*4882a593Smuzhiyun 	for (i = 0; i < 8; i++)
1651*4882a593Smuzhiyun 		if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1652*4882a593Smuzhiyun 			if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1653*4882a593Smuzhiyun 				return cptr[i];
1654*4882a593Smuzhiyun 	return -1;
1655*4882a593Smuzhiyun }
1656*4882a593Smuzhiyun 
speak_highlight(struct vc_data * vc)1657*4882a593Smuzhiyun static int speak_highlight(struct vc_data *vc)
1658*4882a593Smuzhiyun {
1659*4882a593Smuzhiyun 	int hc, d;
1660*4882a593Smuzhiyun 	int vc_num = vc->vc_num;
1661*4882a593Smuzhiyun 
1662*4882a593Smuzhiyun 	if (count_highlight_color(vc) == 1)
1663*4882a593Smuzhiyun 		return 0;
1664*4882a593Smuzhiyun 	hc = get_highlight_color(vc);
1665*4882a593Smuzhiyun 	if (hc != -1) {
1666*4882a593Smuzhiyun 		d = vc->state.y - speakup_console[vc_num]->ht.cy;
1667*4882a593Smuzhiyun 		if ((d == 1) || (d == -1))
1668*4882a593Smuzhiyun 			if (speakup_console[vc_num]->ht.ry[hc] != vc->state.y)
1669*4882a593Smuzhiyun 				return 0;
1670*4882a593Smuzhiyun 		spk_parked |= 0x01;
1671*4882a593Smuzhiyun 		spk_do_flush();
1672*4882a593Smuzhiyun 		spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
1673*4882a593Smuzhiyun 			    speakup_console[vc_num]->ht.highsize[hc]);
1674*4882a593Smuzhiyun 		spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1675*4882a593Smuzhiyun 		spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1676*4882a593Smuzhiyun 		spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1677*4882a593Smuzhiyun 		return 1;
1678*4882a593Smuzhiyun 	}
1679*4882a593Smuzhiyun 	return 0;
1680*4882a593Smuzhiyun }
1681*4882a593Smuzhiyun 
cursor_done(struct timer_list * unused)1682*4882a593Smuzhiyun static void cursor_done(struct timer_list *unused)
1683*4882a593Smuzhiyun {
1684*4882a593Smuzhiyun 	struct vc_data *vc = vc_cons[cursor_con].d;
1685*4882a593Smuzhiyun 	unsigned long flags;
1686*4882a593Smuzhiyun 
1687*4882a593Smuzhiyun 	del_timer(&cursor_timer);
1688*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
1689*4882a593Smuzhiyun 	if (cursor_con != fg_console) {
1690*4882a593Smuzhiyun 		is_cursor = 0;
1691*4882a593Smuzhiyun 		goto out;
1692*4882a593Smuzhiyun 	}
1693*4882a593Smuzhiyun 	speakup_date(vc);
1694*4882a593Smuzhiyun 	if (win_enabled) {
1695*4882a593Smuzhiyun 		if (vc->state.x >= win_left && vc->state.x <= win_right &&
1696*4882a593Smuzhiyun 		    vc->state.y >= win_top && vc->state.y <= win_bottom) {
1697*4882a593Smuzhiyun 			spk_keydown = 0;
1698*4882a593Smuzhiyun 			is_cursor = 0;
1699*4882a593Smuzhiyun 			goto out;
1700*4882a593Smuzhiyun 		}
1701*4882a593Smuzhiyun 	}
1702*4882a593Smuzhiyun 	if (cursor_track == read_all_mode) {
1703*4882a593Smuzhiyun 		handle_cursor_read_all(vc, read_all_key);
1704*4882a593Smuzhiyun 		goto out;
1705*4882a593Smuzhiyun 	}
1706*4882a593Smuzhiyun 	if (cursor_track == CT_Highlight) {
1707*4882a593Smuzhiyun 		if (speak_highlight(vc)) {
1708*4882a593Smuzhiyun 			spk_keydown = 0;
1709*4882a593Smuzhiyun 			is_cursor = 0;
1710*4882a593Smuzhiyun 			goto out;
1711*4882a593Smuzhiyun 		}
1712*4882a593Smuzhiyun 	}
1713*4882a593Smuzhiyun 	if (cursor_track == CT_Window)
1714*4882a593Smuzhiyun 		speakup_win_say(vc);
1715*4882a593Smuzhiyun 	else if (is_cursor == 1 || is_cursor == 4)
1716*4882a593Smuzhiyun 		say_line_from_to(vc, 0, vc->vc_cols, 0);
1717*4882a593Smuzhiyun 	else
1718*4882a593Smuzhiyun 		say_char(vc);
1719*4882a593Smuzhiyun 	spk_keydown = 0;
1720*4882a593Smuzhiyun 	is_cursor = 0;
1721*4882a593Smuzhiyun out:
1722*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1723*4882a593Smuzhiyun }
1724*4882a593Smuzhiyun 
1725*4882a593Smuzhiyun /* called by: vt_notifier_call() */
speakup_bs(struct vc_data * vc)1726*4882a593Smuzhiyun static void speakup_bs(struct vc_data *vc)
1727*4882a593Smuzhiyun {
1728*4882a593Smuzhiyun 	unsigned long flags;
1729*4882a593Smuzhiyun 
1730*4882a593Smuzhiyun 	if (!speakup_console[vc->vc_num])
1731*4882a593Smuzhiyun 		return;
1732*4882a593Smuzhiyun 	if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1733*4882a593Smuzhiyun 		/* Speakup output, discard */
1734*4882a593Smuzhiyun 		return;
1735*4882a593Smuzhiyun 	if (!spk_parked)
1736*4882a593Smuzhiyun 		speakup_date(vc);
1737*4882a593Smuzhiyun 	if (spk_shut_up || !synth) {
1738*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1739*4882a593Smuzhiyun 		return;
1740*4882a593Smuzhiyun 	}
1741*4882a593Smuzhiyun 	if (vc->vc_num == fg_console && spk_keydown) {
1742*4882a593Smuzhiyun 		spk_keydown = 0;
1743*4882a593Smuzhiyun 		if (!is_cursor)
1744*4882a593Smuzhiyun 			say_char(vc);
1745*4882a593Smuzhiyun 	}
1746*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1747*4882a593Smuzhiyun }
1748*4882a593Smuzhiyun 
1749*4882a593Smuzhiyun /* called by: vt_notifier_call() */
speakup_con_write(struct vc_data * vc,u16 * str,int len)1750*4882a593Smuzhiyun static void speakup_con_write(struct vc_data *vc, u16 *str, int len)
1751*4882a593Smuzhiyun {
1752*4882a593Smuzhiyun 	unsigned long flags;
1753*4882a593Smuzhiyun 
1754*4882a593Smuzhiyun 	if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
1755*4882a593Smuzhiyun 		return;
1756*4882a593Smuzhiyun 	if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1757*4882a593Smuzhiyun 		/* Speakup output, discard */
1758*4882a593Smuzhiyun 		return;
1759*4882a593Smuzhiyun 	if (spk_bell_pos && spk_keydown && (vc->state.x == spk_bell_pos - 1))
1760*4882a593Smuzhiyun 		bleep(3);
1761*4882a593Smuzhiyun 	if ((is_cursor) || (cursor_track == read_all_mode)) {
1762*4882a593Smuzhiyun 		if (cursor_track == CT_Highlight)
1763*4882a593Smuzhiyun 			update_color_buffer(vc, str, len);
1764*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1765*4882a593Smuzhiyun 		return;
1766*4882a593Smuzhiyun 	}
1767*4882a593Smuzhiyun 	if (win_enabled) {
1768*4882a593Smuzhiyun 		if (vc->state.x >= win_left && vc->state.x <= win_right &&
1769*4882a593Smuzhiyun 		    vc->state.y >= win_top && vc->state.y <= win_bottom) {
1770*4882a593Smuzhiyun 			spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1771*4882a593Smuzhiyun 			return;
1772*4882a593Smuzhiyun 		}
1773*4882a593Smuzhiyun 	}
1774*4882a593Smuzhiyun 
1775*4882a593Smuzhiyun 	spkup_write(str, len);
1776*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1777*4882a593Smuzhiyun }
1778*4882a593Smuzhiyun 
speakup_con_update(struct vc_data * vc)1779*4882a593Smuzhiyun static void speakup_con_update(struct vc_data *vc)
1780*4882a593Smuzhiyun {
1781*4882a593Smuzhiyun 	unsigned long flags;
1782*4882a593Smuzhiyun 
1783*4882a593Smuzhiyun 	if (!speakup_console[vc->vc_num] || spk_parked || !synth)
1784*4882a593Smuzhiyun 		return;
1785*4882a593Smuzhiyun 	if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1786*4882a593Smuzhiyun 		/* Speakup output, discard */
1787*4882a593Smuzhiyun 		return;
1788*4882a593Smuzhiyun 	speakup_date(vc);
1789*4882a593Smuzhiyun 	if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) {
1790*4882a593Smuzhiyun 		synth_printf("%s", spk_str_pause);
1791*4882a593Smuzhiyun 		spk_paused = true;
1792*4882a593Smuzhiyun 	}
1793*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1794*4882a593Smuzhiyun }
1795*4882a593Smuzhiyun 
do_handle_spec(struct vc_data * vc,u_char value,char up_flag)1796*4882a593Smuzhiyun static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1797*4882a593Smuzhiyun {
1798*4882a593Smuzhiyun 	unsigned long flags;
1799*4882a593Smuzhiyun 	int on_off = 2;
1800*4882a593Smuzhiyun 	char *label;
1801*4882a593Smuzhiyun 
1802*4882a593Smuzhiyun 	if (!synth || up_flag || spk_killed)
1803*4882a593Smuzhiyun 		return;
1804*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
1805*4882a593Smuzhiyun 	spk_shut_up &= 0xfe;
1806*4882a593Smuzhiyun 	if (spk_no_intr)
1807*4882a593Smuzhiyun 		spk_do_flush();
1808*4882a593Smuzhiyun 	switch (value) {
1809*4882a593Smuzhiyun 	case KVAL(K_CAPS):
1810*4882a593Smuzhiyun 		label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
1811*4882a593Smuzhiyun 		on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
1812*4882a593Smuzhiyun 		break;
1813*4882a593Smuzhiyun 	case KVAL(K_NUM):
1814*4882a593Smuzhiyun 		label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
1815*4882a593Smuzhiyun 		on_off = vt_get_leds(fg_console, VC_NUMLOCK);
1816*4882a593Smuzhiyun 		break;
1817*4882a593Smuzhiyun 	case KVAL(K_HOLD):
1818*4882a593Smuzhiyun 		label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
1819*4882a593Smuzhiyun 		on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
1820*4882a593Smuzhiyun 		if (speakup_console[vc->vc_num])
1821*4882a593Smuzhiyun 			speakup_console[vc->vc_num]->tty_stopped = on_off;
1822*4882a593Smuzhiyun 		break;
1823*4882a593Smuzhiyun 	default:
1824*4882a593Smuzhiyun 		spk_parked &= 0xfe;
1825*4882a593Smuzhiyun 		spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1826*4882a593Smuzhiyun 		return;
1827*4882a593Smuzhiyun 	}
1828*4882a593Smuzhiyun 	if (on_off < 2)
1829*4882a593Smuzhiyun 		synth_printf("%s %s\n",
1830*4882a593Smuzhiyun 			     label, spk_msg_get(MSG_STATUS_START + on_off));
1831*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1832*4882a593Smuzhiyun }
1833*4882a593Smuzhiyun 
inc_dec_var(u_char value)1834*4882a593Smuzhiyun static int inc_dec_var(u_char value)
1835*4882a593Smuzhiyun {
1836*4882a593Smuzhiyun 	struct st_var_header *p_header;
1837*4882a593Smuzhiyun 	struct var_t *var_data;
1838*4882a593Smuzhiyun 	char num_buf[32];
1839*4882a593Smuzhiyun 	char *cp = num_buf;
1840*4882a593Smuzhiyun 	char *pn;
1841*4882a593Smuzhiyun 	int var_id = (int)value - VAR_START;
1842*4882a593Smuzhiyun 	int how = (var_id & 1) ? E_INC : E_DEC;
1843*4882a593Smuzhiyun 
1844*4882a593Smuzhiyun 	var_id = var_id / 2 + FIRST_SET_VAR;
1845*4882a593Smuzhiyun 	p_header = spk_get_var_header(var_id);
1846*4882a593Smuzhiyun 	if (!p_header)
1847*4882a593Smuzhiyun 		return -1;
1848*4882a593Smuzhiyun 	if (p_header->var_type != VAR_NUM)
1849*4882a593Smuzhiyun 		return -1;
1850*4882a593Smuzhiyun 	var_data = p_header->data;
1851*4882a593Smuzhiyun 	if (spk_set_num_var(1, p_header, how) != 0)
1852*4882a593Smuzhiyun 		return -1;
1853*4882a593Smuzhiyun 	if (!spk_close_press) {
1854*4882a593Smuzhiyun 		for (pn = p_header->name; *pn; pn++) {
1855*4882a593Smuzhiyun 			if (*pn == '_')
1856*4882a593Smuzhiyun 				*cp = SPACE;
1857*4882a593Smuzhiyun 			else
1858*4882a593Smuzhiyun 				*cp++ = *pn;
1859*4882a593Smuzhiyun 		}
1860*4882a593Smuzhiyun 	}
1861*4882a593Smuzhiyun 	snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
1862*4882a593Smuzhiyun 		 var_data->u.n.value);
1863*4882a593Smuzhiyun 	synth_printf("%s", num_buf);
1864*4882a593Smuzhiyun 	return 0;
1865*4882a593Smuzhiyun }
1866*4882a593Smuzhiyun 
speakup_win_set(struct vc_data * vc)1867*4882a593Smuzhiyun static void speakup_win_set(struct vc_data *vc)
1868*4882a593Smuzhiyun {
1869*4882a593Smuzhiyun 	char info[40];
1870*4882a593Smuzhiyun 
1871*4882a593Smuzhiyun 	if (win_start > 1) {
1872*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
1873*4882a593Smuzhiyun 		return;
1874*4882a593Smuzhiyun 	}
1875*4882a593Smuzhiyun 	if (spk_x < win_left || spk_y < win_top) {
1876*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
1877*4882a593Smuzhiyun 		return;
1878*4882a593Smuzhiyun 	}
1879*4882a593Smuzhiyun 	if (win_start && spk_x == win_left && spk_y == win_top) {
1880*4882a593Smuzhiyun 		win_left = 0;
1881*4882a593Smuzhiyun 		win_right = vc->vc_cols - 1;
1882*4882a593Smuzhiyun 		win_bottom = spk_y;
1883*4882a593Smuzhiyun 		snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
1884*4882a593Smuzhiyun 			 (int)win_top + 1);
1885*4882a593Smuzhiyun 	} else {
1886*4882a593Smuzhiyun 		if (!win_start) {
1887*4882a593Smuzhiyun 			win_top = spk_y;
1888*4882a593Smuzhiyun 			win_left = spk_x;
1889*4882a593Smuzhiyun 		} else {
1890*4882a593Smuzhiyun 			win_bottom = spk_y;
1891*4882a593Smuzhiyun 			win_right = spk_x;
1892*4882a593Smuzhiyun 		}
1893*4882a593Smuzhiyun 		snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
1894*4882a593Smuzhiyun 			 (win_start) ?
1895*4882a593Smuzhiyun 				spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
1896*4882a593Smuzhiyun 			 (int)spk_y + 1, (int)spk_x + 1);
1897*4882a593Smuzhiyun 	}
1898*4882a593Smuzhiyun 	synth_printf("%s\n", info);
1899*4882a593Smuzhiyun 	win_start++;
1900*4882a593Smuzhiyun }
1901*4882a593Smuzhiyun 
speakup_win_clear(struct vc_data * vc)1902*4882a593Smuzhiyun static void speakup_win_clear(struct vc_data *vc)
1903*4882a593Smuzhiyun {
1904*4882a593Smuzhiyun 	win_top = 0;
1905*4882a593Smuzhiyun 	win_bottom = 0;
1906*4882a593Smuzhiyun 	win_left = 0;
1907*4882a593Smuzhiyun 	win_right = 0;
1908*4882a593Smuzhiyun 	win_start = 0;
1909*4882a593Smuzhiyun 	synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
1910*4882a593Smuzhiyun }
1911*4882a593Smuzhiyun 
speakup_win_enable(struct vc_data * vc)1912*4882a593Smuzhiyun static void speakup_win_enable(struct vc_data *vc)
1913*4882a593Smuzhiyun {
1914*4882a593Smuzhiyun 	if (win_start < 2) {
1915*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
1916*4882a593Smuzhiyun 		return;
1917*4882a593Smuzhiyun 	}
1918*4882a593Smuzhiyun 	win_enabled ^= 1;
1919*4882a593Smuzhiyun 	if (win_enabled)
1920*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
1921*4882a593Smuzhiyun 	else
1922*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
1923*4882a593Smuzhiyun }
1924*4882a593Smuzhiyun 
speakup_bits(struct vc_data * vc)1925*4882a593Smuzhiyun static void speakup_bits(struct vc_data *vc)
1926*4882a593Smuzhiyun {
1927*4882a593Smuzhiyun 	int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
1928*4882a593Smuzhiyun 
1929*4882a593Smuzhiyun 	if (spk_special_handler || val < 1 || val > 6) {
1930*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_ERROR));
1931*4882a593Smuzhiyun 		return;
1932*4882a593Smuzhiyun 	}
1933*4882a593Smuzhiyun 	pb_edit = &spk_punc_info[val];
1934*4882a593Smuzhiyun 	synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1935*4882a593Smuzhiyun 	spk_special_handler = edit_bits;
1936*4882a593Smuzhiyun }
1937*4882a593Smuzhiyun 
handle_goto(struct vc_data * vc,u_char type,u_char ch,u_short key)1938*4882a593Smuzhiyun static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1939*4882a593Smuzhiyun {
1940*4882a593Smuzhiyun 	static u_char goto_buf[8];
1941*4882a593Smuzhiyun 	static int num;
1942*4882a593Smuzhiyun 	int maxlen;
1943*4882a593Smuzhiyun 	char *cp;
1944*4882a593Smuzhiyun 	u16 wch;
1945*4882a593Smuzhiyun 
1946*4882a593Smuzhiyun 	if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1947*4882a593Smuzhiyun 		goto do_goto;
1948*4882a593Smuzhiyun 	if (type == KT_LATIN && ch == '\n')
1949*4882a593Smuzhiyun 		goto do_goto;
1950*4882a593Smuzhiyun 	if (type != 0)
1951*4882a593Smuzhiyun 		goto oops;
1952*4882a593Smuzhiyun 	if (ch == 8) {
1953*4882a593Smuzhiyun 		u16 wch;
1954*4882a593Smuzhiyun 
1955*4882a593Smuzhiyun 		if (num == 0)
1956*4882a593Smuzhiyun 			return -1;
1957*4882a593Smuzhiyun 		wch = goto_buf[--num];
1958*4882a593Smuzhiyun 		goto_buf[num] = '\0';
1959*4882a593Smuzhiyun 		spkup_write(&wch, 1);
1960*4882a593Smuzhiyun 		return 1;
1961*4882a593Smuzhiyun 	}
1962*4882a593Smuzhiyun 	if (ch < '+' || ch > 'y')
1963*4882a593Smuzhiyun 		goto oops;
1964*4882a593Smuzhiyun 	wch = ch;
1965*4882a593Smuzhiyun 	goto_buf[num++] = ch;
1966*4882a593Smuzhiyun 	goto_buf[num] = '\0';
1967*4882a593Smuzhiyun 	spkup_write(&wch, 1);
1968*4882a593Smuzhiyun 	maxlen = (*goto_buf >= '0') ? 3 : 4;
1969*4882a593Smuzhiyun 	if ((ch == '+' || ch == '-') && num == 1)
1970*4882a593Smuzhiyun 		return 1;
1971*4882a593Smuzhiyun 	if (ch >= '0' && ch <= '9' && num < maxlen)
1972*4882a593Smuzhiyun 		return 1;
1973*4882a593Smuzhiyun 	if (num < maxlen - 1 || num > maxlen)
1974*4882a593Smuzhiyun 		goto oops;
1975*4882a593Smuzhiyun 	if (ch < 'x' || ch > 'y') {
1976*4882a593Smuzhiyun oops:
1977*4882a593Smuzhiyun 		if (!spk_killed)
1978*4882a593Smuzhiyun 			synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
1979*4882a593Smuzhiyun 		goto_buf[num = 0] = '\0';
1980*4882a593Smuzhiyun 		spk_special_handler = NULL;
1981*4882a593Smuzhiyun 		return 1;
1982*4882a593Smuzhiyun 	}
1983*4882a593Smuzhiyun 
1984*4882a593Smuzhiyun 	/* Do not replace with kstrtoul: here we need cp to be updated */
1985*4882a593Smuzhiyun 	goto_pos = simple_strtoul(goto_buf, &cp, 10);
1986*4882a593Smuzhiyun 
1987*4882a593Smuzhiyun 	if (*cp == 'x') {
1988*4882a593Smuzhiyun 		if (*goto_buf < '0')
1989*4882a593Smuzhiyun 			goto_pos += spk_x;
1990*4882a593Smuzhiyun 		else if (goto_pos > 0)
1991*4882a593Smuzhiyun 			goto_pos--;
1992*4882a593Smuzhiyun 
1993*4882a593Smuzhiyun 		if (goto_pos >= vc->vc_cols)
1994*4882a593Smuzhiyun 			goto_pos = vc->vc_cols - 1;
1995*4882a593Smuzhiyun 		goto_x = 1;
1996*4882a593Smuzhiyun 	} else {
1997*4882a593Smuzhiyun 		if (*goto_buf < '0')
1998*4882a593Smuzhiyun 			goto_pos += spk_y;
1999*4882a593Smuzhiyun 		else if (goto_pos > 0)
2000*4882a593Smuzhiyun 			goto_pos--;
2001*4882a593Smuzhiyun 
2002*4882a593Smuzhiyun 		if (goto_pos >= vc->vc_rows)
2003*4882a593Smuzhiyun 			goto_pos = vc->vc_rows - 1;
2004*4882a593Smuzhiyun 		goto_x = 0;
2005*4882a593Smuzhiyun 	}
2006*4882a593Smuzhiyun 	goto_buf[num = 0] = '\0';
2007*4882a593Smuzhiyun do_goto:
2008*4882a593Smuzhiyun 	spk_special_handler = NULL;
2009*4882a593Smuzhiyun 	spk_parked |= 0x01;
2010*4882a593Smuzhiyun 	if (goto_x) {
2011*4882a593Smuzhiyun 		spk_pos -= spk_x * 2;
2012*4882a593Smuzhiyun 		spk_x = goto_pos;
2013*4882a593Smuzhiyun 		spk_pos += goto_pos * 2;
2014*4882a593Smuzhiyun 		say_word(vc);
2015*4882a593Smuzhiyun 	} else {
2016*4882a593Smuzhiyun 		spk_y = goto_pos;
2017*4882a593Smuzhiyun 		spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
2018*4882a593Smuzhiyun 		say_line(vc);
2019*4882a593Smuzhiyun 	}
2020*4882a593Smuzhiyun 	return 1;
2021*4882a593Smuzhiyun }
2022*4882a593Smuzhiyun 
speakup_goto(struct vc_data * vc)2023*4882a593Smuzhiyun static void speakup_goto(struct vc_data *vc)
2024*4882a593Smuzhiyun {
2025*4882a593Smuzhiyun 	if (spk_special_handler) {
2026*4882a593Smuzhiyun 		synth_printf("%s\n", spk_msg_get(MSG_ERROR));
2027*4882a593Smuzhiyun 		return;
2028*4882a593Smuzhiyun 	}
2029*4882a593Smuzhiyun 	synth_printf("%s\n", spk_msg_get(MSG_GOTO));
2030*4882a593Smuzhiyun 	spk_special_handler = handle_goto;
2031*4882a593Smuzhiyun }
2032*4882a593Smuzhiyun 
speakup_help(struct vc_data * vc)2033*4882a593Smuzhiyun static void speakup_help(struct vc_data *vc)
2034*4882a593Smuzhiyun {
2035*4882a593Smuzhiyun 	spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
2036*4882a593Smuzhiyun }
2037*4882a593Smuzhiyun 
do_nothing(struct vc_data * vc)2038*4882a593Smuzhiyun static void do_nothing(struct vc_data *vc)
2039*4882a593Smuzhiyun {
2040*4882a593Smuzhiyun 	return;			/* flush done in do_spkup */
2041*4882a593Smuzhiyun }
2042*4882a593Smuzhiyun 
2043*4882a593Smuzhiyun static u_char key_speakup, spk_key_locked;
2044*4882a593Smuzhiyun 
speakup_lock(struct vc_data * vc)2045*4882a593Smuzhiyun static void speakup_lock(struct vc_data *vc)
2046*4882a593Smuzhiyun {
2047*4882a593Smuzhiyun 	if (!spk_key_locked) {
2048*4882a593Smuzhiyun 		spk_key_locked = 16;
2049*4882a593Smuzhiyun 		key_speakup = 16;
2050*4882a593Smuzhiyun 	} else {
2051*4882a593Smuzhiyun 		spk_key_locked = 0;
2052*4882a593Smuzhiyun 		key_speakup = 0;
2053*4882a593Smuzhiyun 	}
2054*4882a593Smuzhiyun }
2055*4882a593Smuzhiyun 
2056*4882a593Smuzhiyun typedef void (*spkup_hand) (struct vc_data *);
2057*4882a593Smuzhiyun static spkup_hand spkup_handler[] = {
2058*4882a593Smuzhiyun 	/* must be ordered same as defines in speakup.h */
2059*4882a593Smuzhiyun 	do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2060*4882a593Smuzhiyun 	speakup_cut, speakup_paste, say_first_char, say_last_char,
2061*4882a593Smuzhiyun 	say_char, say_prev_char, say_next_char,
2062*4882a593Smuzhiyun 	say_word, say_prev_word, say_next_word,
2063*4882a593Smuzhiyun 	say_line, say_prev_line, say_next_line,
2064*4882a593Smuzhiyun 	top_edge, bottom_edge, left_edge, right_edge,
2065*4882a593Smuzhiyun 	spell_word, spell_word, say_screen,
2066*4882a593Smuzhiyun 	say_position, say_attributes,
2067*4882a593Smuzhiyun 	speakup_off, speakup_parked, say_line,	/* this is for indent */
2068*4882a593Smuzhiyun 	say_from_top, say_to_bottom,
2069*4882a593Smuzhiyun 	say_from_left, say_to_right,
2070*4882a593Smuzhiyun 	say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2071*4882a593Smuzhiyun 	speakup_bits, speakup_bits, speakup_bits,
2072*4882a593Smuzhiyun 	speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2073*4882a593Smuzhiyun 	speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2074*4882a593Smuzhiyun };
2075*4882a593Smuzhiyun 
do_spkup(struct vc_data * vc,u_char value)2076*4882a593Smuzhiyun static void do_spkup(struct vc_data *vc, u_char value)
2077*4882a593Smuzhiyun {
2078*4882a593Smuzhiyun 	if (spk_killed && value != SPEECH_KILL)
2079*4882a593Smuzhiyun 		return;
2080*4882a593Smuzhiyun 	spk_keydown = 0;
2081*4882a593Smuzhiyun 	spk_lastkey = 0;
2082*4882a593Smuzhiyun 	spk_shut_up &= 0xfe;
2083*4882a593Smuzhiyun 	this_speakup_key = value;
2084*4882a593Smuzhiyun 	if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
2085*4882a593Smuzhiyun 		spk_do_flush();
2086*4882a593Smuzhiyun 		(*spkup_handler[value]) (vc);
2087*4882a593Smuzhiyun 	} else {
2088*4882a593Smuzhiyun 		if (inc_dec_var(value) < 0)
2089*4882a593Smuzhiyun 			bleep(9);
2090*4882a593Smuzhiyun 	}
2091*4882a593Smuzhiyun }
2092*4882a593Smuzhiyun 
2093*4882a593Smuzhiyun static const char *pad_chars = "0123456789+-*/\015,.?()";
2094*4882a593Smuzhiyun 
2095*4882a593Smuzhiyun static int
speakup_key(struct vc_data * vc,int shift_state,int keycode,u_short keysym,int up_flag)2096*4882a593Smuzhiyun speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
2097*4882a593Smuzhiyun 	    int up_flag)
2098*4882a593Smuzhiyun {
2099*4882a593Smuzhiyun 	unsigned long flags;
2100*4882a593Smuzhiyun 	int kh;
2101*4882a593Smuzhiyun 	u_char *key_info;
2102*4882a593Smuzhiyun 	u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2103*4882a593Smuzhiyun 	u_char shift_info, offset;
2104*4882a593Smuzhiyun 	int ret = 0;
2105*4882a593Smuzhiyun 
2106*4882a593Smuzhiyun 	if (!synth)
2107*4882a593Smuzhiyun 		return 0;
2108*4882a593Smuzhiyun 
2109*4882a593Smuzhiyun 	spin_lock_irqsave(&speakup_info.spinlock, flags);
2110*4882a593Smuzhiyun 	tty = vc->port.tty;
2111*4882a593Smuzhiyun 	if (type >= 0xf0)
2112*4882a593Smuzhiyun 		type -= 0xf0;
2113*4882a593Smuzhiyun 	if (type == KT_PAD &&
2114*4882a593Smuzhiyun 	    (vt_get_leds(fg_console, VC_NUMLOCK))) {
2115*4882a593Smuzhiyun 		if (up_flag) {
2116*4882a593Smuzhiyun 			spk_keydown = 0;
2117*4882a593Smuzhiyun 			goto out;
2118*4882a593Smuzhiyun 		}
2119*4882a593Smuzhiyun 		value = pad_chars[value];
2120*4882a593Smuzhiyun 		spk_lastkey = value;
2121*4882a593Smuzhiyun 		spk_keydown++;
2122*4882a593Smuzhiyun 		spk_parked &= 0xfe;
2123*4882a593Smuzhiyun 		goto no_map;
2124*4882a593Smuzhiyun 	}
2125*4882a593Smuzhiyun 	if (keycode >= MAX_KEY)
2126*4882a593Smuzhiyun 		goto no_map;
2127*4882a593Smuzhiyun 	key_info = spk_our_keys[keycode];
2128*4882a593Smuzhiyun 	if (!key_info)
2129*4882a593Smuzhiyun 		goto no_map;
2130*4882a593Smuzhiyun 	/* Check valid read all mode keys */
2131*4882a593Smuzhiyun 	if ((cursor_track == read_all_mode) && (!up_flag)) {
2132*4882a593Smuzhiyun 		switch (value) {
2133*4882a593Smuzhiyun 		case KVAL(K_DOWN):
2134*4882a593Smuzhiyun 		case KVAL(K_UP):
2135*4882a593Smuzhiyun 		case KVAL(K_LEFT):
2136*4882a593Smuzhiyun 		case KVAL(K_RIGHT):
2137*4882a593Smuzhiyun 		case KVAL(K_PGUP):
2138*4882a593Smuzhiyun 		case KVAL(K_PGDN):
2139*4882a593Smuzhiyun 			break;
2140*4882a593Smuzhiyun 		default:
2141*4882a593Smuzhiyun 			stop_read_all(vc);
2142*4882a593Smuzhiyun 			break;
2143*4882a593Smuzhiyun 		}
2144*4882a593Smuzhiyun 	}
2145*4882a593Smuzhiyun 	shift_info = (shift_state & 0x0f) + key_speakup;
2146*4882a593Smuzhiyun 	offset = spk_shift_table[shift_info];
2147*4882a593Smuzhiyun 	if (offset) {
2148*4882a593Smuzhiyun 		new_key = key_info[offset];
2149*4882a593Smuzhiyun 		if (new_key) {
2150*4882a593Smuzhiyun 			ret = 1;
2151*4882a593Smuzhiyun 			if (new_key == SPK_KEY) {
2152*4882a593Smuzhiyun 				if (!spk_key_locked)
2153*4882a593Smuzhiyun 					key_speakup = (up_flag) ? 0 : 16;
2154*4882a593Smuzhiyun 				if (up_flag || spk_killed)
2155*4882a593Smuzhiyun 					goto out;
2156*4882a593Smuzhiyun 				spk_shut_up &= 0xfe;
2157*4882a593Smuzhiyun 				spk_do_flush();
2158*4882a593Smuzhiyun 				goto out;
2159*4882a593Smuzhiyun 			}
2160*4882a593Smuzhiyun 			if (up_flag)
2161*4882a593Smuzhiyun 				goto out;
2162*4882a593Smuzhiyun 			if (last_keycode == keycode &&
2163*4882a593Smuzhiyun 			    time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
2164*4882a593Smuzhiyun 				spk_close_press = 1;
2165*4882a593Smuzhiyun 				offset = spk_shift_table[shift_info + 32];
2166*4882a593Smuzhiyun 				/* double press? */
2167*4882a593Smuzhiyun 				if (offset && key_info[offset])
2168*4882a593Smuzhiyun 					new_key = key_info[offset];
2169*4882a593Smuzhiyun 			}
2170*4882a593Smuzhiyun 			last_keycode = keycode;
2171*4882a593Smuzhiyun 			last_spk_jiffy = jiffies;
2172*4882a593Smuzhiyun 			type = KT_SPKUP;
2173*4882a593Smuzhiyun 			value = new_key;
2174*4882a593Smuzhiyun 		}
2175*4882a593Smuzhiyun 	}
2176*4882a593Smuzhiyun no_map:
2177*4882a593Smuzhiyun 	if (type == KT_SPKUP && !spk_special_handler) {
2178*4882a593Smuzhiyun 		do_spkup(vc, new_key);
2179*4882a593Smuzhiyun 		spk_close_press = 0;
2180*4882a593Smuzhiyun 		ret = 1;
2181*4882a593Smuzhiyun 		goto out;
2182*4882a593Smuzhiyun 	}
2183*4882a593Smuzhiyun 	if (up_flag || spk_killed || type == KT_SHIFT)
2184*4882a593Smuzhiyun 		goto out;
2185*4882a593Smuzhiyun 	spk_shut_up &= 0xfe;
2186*4882a593Smuzhiyun 	kh = (value == KVAL(K_DOWN)) ||
2187*4882a593Smuzhiyun 	    (value == KVAL(K_UP)) ||
2188*4882a593Smuzhiyun 	    (value == KVAL(K_LEFT)) ||
2189*4882a593Smuzhiyun 	    (value == KVAL(K_RIGHT));
2190*4882a593Smuzhiyun 	if ((cursor_track != read_all_mode) || !kh)
2191*4882a593Smuzhiyun 		if (!spk_no_intr)
2192*4882a593Smuzhiyun 			spk_do_flush();
2193*4882a593Smuzhiyun 	if (spk_special_handler) {
2194*4882a593Smuzhiyun 		if (type == KT_SPEC && value == 1) {
2195*4882a593Smuzhiyun 			value = '\n';
2196*4882a593Smuzhiyun 			type = KT_LATIN;
2197*4882a593Smuzhiyun 		} else if (type == KT_LETTER) {
2198*4882a593Smuzhiyun 			type = KT_LATIN;
2199*4882a593Smuzhiyun 		} else if (value == 0x7f) {
2200*4882a593Smuzhiyun 			value = 8;	/* make del = backspace */
2201*4882a593Smuzhiyun 		}
2202*4882a593Smuzhiyun 		ret = (*spk_special_handler) (vc, type, value, keycode);
2203*4882a593Smuzhiyun 		spk_close_press = 0;
2204*4882a593Smuzhiyun 		if (ret < 0)
2205*4882a593Smuzhiyun 			bleep(9);
2206*4882a593Smuzhiyun 		goto out;
2207*4882a593Smuzhiyun 	}
2208*4882a593Smuzhiyun 	last_keycode = 0;
2209*4882a593Smuzhiyun out:
2210*4882a593Smuzhiyun 	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2211*4882a593Smuzhiyun 	return ret;
2212*4882a593Smuzhiyun }
2213*4882a593Smuzhiyun 
keyboard_notifier_call(struct notifier_block * nb,unsigned long code,void * _param)2214*4882a593Smuzhiyun static int keyboard_notifier_call(struct notifier_block *nb,
2215*4882a593Smuzhiyun 				  unsigned long code, void *_param)
2216*4882a593Smuzhiyun {
2217*4882a593Smuzhiyun 	struct keyboard_notifier_param *param = _param;
2218*4882a593Smuzhiyun 	struct vc_data *vc = param->vc;
2219*4882a593Smuzhiyun 	int up = !param->down;
2220*4882a593Smuzhiyun 	int ret = NOTIFY_OK;
2221*4882a593Smuzhiyun 	static int keycode;	/* to hold the current keycode */
2222*4882a593Smuzhiyun 
2223*4882a593Smuzhiyun 	in_keyboard_notifier = 1;
2224*4882a593Smuzhiyun 
2225*4882a593Smuzhiyun 	if (vc->vc_mode == KD_GRAPHICS)
2226*4882a593Smuzhiyun 		goto out;
2227*4882a593Smuzhiyun 
2228*4882a593Smuzhiyun 	/*
2229*4882a593Smuzhiyun 	 * First, determine whether we are handling a fake keypress on
2230*4882a593Smuzhiyun 	 * the current processor.  If we are, then return NOTIFY_OK,
2231*4882a593Smuzhiyun 	 * to pass the keystroke up the chain.  This prevents us from
2232*4882a593Smuzhiyun 	 * trying to take the Speakup lock while it is held by the
2233*4882a593Smuzhiyun 	 * processor on which the simulated keystroke was generated.
2234*4882a593Smuzhiyun 	 * Also, the simulated keystrokes should be ignored by Speakup.
2235*4882a593Smuzhiyun 	 */
2236*4882a593Smuzhiyun 
2237*4882a593Smuzhiyun 	if (speakup_fake_key_pressed())
2238*4882a593Smuzhiyun 		goto out;
2239*4882a593Smuzhiyun 
2240*4882a593Smuzhiyun 	switch (code) {
2241*4882a593Smuzhiyun 	case KBD_KEYCODE:
2242*4882a593Smuzhiyun 		/* speakup requires keycode and keysym currently */
2243*4882a593Smuzhiyun 		keycode = param->value;
2244*4882a593Smuzhiyun 		break;
2245*4882a593Smuzhiyun 	case KBD_UNBOUND_KEYCODE:
2246*4882a593Smuzhiyun 		/* not used yet */
2247*4882a593Smuzhiyun 		break;
2248*4882a593Smuzhiyun 	case KBD_UNICODE:
2249*4882a593Smuzhiyun 		/* not used yet */
2250*4882a593Smuzhiyun 		break;
2251*4882a593Smuzhiyun 	case KBD_KEYSYM:
2252*4882a593Smuzhiyun 		if (speakup_key(vc, param->shift, keycode, param->value, up))
2253*4882a593Smuzhiyun 			ret = NOTIFY_STOP;
2254*4882a593Smuzhiyun 		else if (KTYP(param->value) == KT_CUR)
2255*4882a593Smuzhiyun 			ret = pre_handle_cursor(vc, KVAL(param->value), up);
2256*4882a593Smuzhiyun 		break;
2257*4882a593Smuzhiyun 	case KBD_POST_KEYSYM:{
2258*4882a593Smuzhiyun 			unsigned char type = KTYP(param->value) - 0xf0;
2259*4882a593Smuzhiyun 			unsigned char val = KVAL(param->value);
2260*4882a593Smuzhiyun 
2261*4882a593Smuzhiyun 			switch (type) {
2262*4882a593Smuzhiyun 			case KT_SHIFT:
2263*4882a593Smuzhiyun 				do_handle_shift(vc, val, up);
2264*4882a593Smuzhiyun 				break;
2265*4882a593Smuzhiyun 			case KT_LATIN:
2266*4882a593Smuzhiyun 			case KT_LETTER:
2267*4882a593Smuzhiyun 				do_handle_latin(vc, val, up);
2268*4882a593Smuzhiyun 				break;
2269*4882a593Smuzhiyun 			case KT_CUR:
2270*4882a593Smuzhiyun 				do_handle_cursor(vc, val, up);
2271*4882a593Smuzhiyun 				break;
2272*4882a593Smuzhiyun 			case KT_SPEC:
2273*4882a593Smuzhiyun 				do_handle_spec(vc, val, up);
2274*4882a593Smuzhiyun 				break;
2275*4882a593Smuzhiyun 			}
2276*4882a593Smuzhiyun 			break;
2277*4882a593Smuzhiyun 		}
2278*4882a593Smuzhiyun 	}
2279*4882a593Smuzhiyun out:
2280*4882a593Smuzhiyun 	in_keyboard_notifier = 0;
2281*4882a593Smuzhiyun 	return ret;
2282*4882a593Smuzhiyun }
2283*4882a593Smuzhiyun 
vt_notifier_call(struct notifier_block * nb,unsigned long code,void * _param)2284*4882a593Smuzhiyun static int vt_notifier_call(struct notifier_block *nb,
2285*4882a593Smuzhiyun 			    unsigned long code, void *_param)
2286*4882a593Smuzhiyun {
2287*4882a593Smuzhiyun 	struct vt_notifier_param *param = _param;
2288*4882a593Smuzhiyun 	struct vc_data *vc = param->vc;
2289*4882a593Smuzhiyun 
2290*4882a593Smuzhiyun 	switch (code) {
2291*4882a593Smuzhiyun 	case VT_ALLOCATE:
2292*4882a593Smuzhiyun 		if (vc->vc_mode == KD_TEXT)
2293*4882a593Smuzhiyun 			speakup_allocate(vc, GFP_ATOMIC);
2294*4882a593Smuzhiyun 		break;
2295*4882a593Smuzhiyun 	case VT_DEALLOCATE:
2296*4882a593Smuzhiyun 		speakup_deallocate(vc);
2297*4882a593Smuzhiyun 		break;
2298*4882a593Smuzhiyun 	case VT_WRITE:
2299*4882a593Smuzhiyun 		if (param->c == '\b') {
2300*4882a593Smuzhiyun 			speakup_bs(vc);
2301*4882a593Smuzhiyun 		} else {
2302*4882a593Smuzhiyun 			u16 d = param->c;
2303*4882a593Smuzhiyun 
2304*4882a593Smuzhiyun 			speakup_con_write(vc, &d, 1);
2305*4882a593Smuzhiyun 		}
2306*4882a593Smuzhiyun 		break;
2307*4882a593Smuzhiyun 	case VT_UPDATE:
2308*4882a593Smuzhiyun 		speakup_con_update(vc);
2309*4882a593Smuzhiyun 		break;
2310*4882a593Smuzhiyun 	}
2311*4882a593Smuzhiyun 	return NOTIFY_OK;
2312*4882a593Smuzhiyun }
2313*4882a593Smuzhiyun 
2314*4882a593Smuzhiyun /* called by: module_exit() */
speakup_exit(void)2315*4882a593Smuzhiyun static void __exit speakup_exit(void)
2316*4882a593Smuzhiyun {
2317*4882a593Smuzhiyun 	int i;
2318*4882a593Smuzhiyun 
2319*4882a593Smuzhiyun 	unregister_keyboard_notifier(&keyboard_notifier_block);
2320*4882a593Smuzhiyun 	unregister_vt_notifier(&vt_notifier_block);
2321*4882a593Smuzhiyun 	speakup_unregister_devsynth();
2322*4882a593Smuzhiyun 	speakup_cancel_selection();
2323*4882a593Smuzhiyun 	speakup_cancel_paste();
2324*4882a593Smuzhiyun 	del_timer_sync(&cursor_timer);
2325*4882a593Smuzhiyun 	kthread_stop(speakup_task);
2326*4882a593Smuzhiyun 	speakup_task = NULL;
2327*4882a593Smuzhiyun 	mutex_lock(&spk_mutex);
2328*4882a593Smuzhiyun 	synth_release();
2329*4882a593Smuzhiyun 	mutex_unlock(&spk_mutex);
2330*4882a593Smuzhiyun 	spk_ttyio_unregister_ldisc();
2331*4882a593Smuzhiyun 
2332*4882a593Smuzhiyun 	speakup_kobj_exit();
2333*4882a593Smuzhiyun 
2334*4882a593Smuzhiyun 	for (i = 0; i < MAX_NR_CONSOLES; i++)
2335*4882a593Smuzhiyun 		kfree(speakup_console[i]);
2336*4882a593Smuzhiyun 
2337*4882a593Smuzhiyun 	speakup_remove_virtual_keyboard();
2338*4882a593Smuzhiyun 
2339*4882a593Smuzhiyun 	for (i = 0; i < MAXVARS; i++)
2340*4882a593Smuzhiyun 		speakup_unregister_var(i);
2341*4882a593Smuzhiyun 
2342*4882a593Smuzhiyun 	for (i = 0; i < 256; i++) {
2343*4882a593Smuzhiyun 		if (spk_characters[i] != spk_default_chars[i])
2344*4882a593Smuzhiyun 			kfree(spk_characters[i]);
2345*4882a593Smuzhiyun 	}
2346*4882a593Smuzhiyun 
2347*4882a593Smuzhiyun 	spk_free_user_msgs();
2348*4882a593Smuzhiyun }
2349*4882a593Smuzhiyun 
2350*4882a593Smuzhiyun /* call by: module_init() */
speakup_init(void)2351*4882a593Smuzhiyun static int __init speakup_init(void)
2352*4882a593Smuzhiyun {
2353*4882a593Smuzhiyun 	int i;
2354*4882a593Smuzhiyun 	long err = 0;
2355*4882a593Smuzhiyun 	struct vc_data *vc = vc_cons[fg_console].d;
2356*4882a593Smuzhiyun 	struct var_t *var;
2357*4882a593Smuzhiyun 
2358*4882a593Smuzhiyun 	/* These first few initializations cannot fail. */
2359*4882a593Smuzhiyun 	spk_initialize_msgs();	/* Initialize arrays for i18n. */
2360*4882a593Smuzhiyun 	spk_reset_default_chars();
2361*4882a593Smuzhiyun 	spk_reset_default_chartab();
2362*4882a593Smuzhiyun 	spk_strlwr(synth_name);
2363*4882a593Smuzhiyun 	spk_vars[0].u.n.high = vc->vc_cols;
2364*4882a593Smuzhiyun 	for (var = spk_vars; var->var_id != MAXVARS; var++)
2365*4882a593Smuzhiyun 		speakup_register_var(var);
2366*4882a593Smuzhiyun 	for (var = synth_time_vars;
2367*4882a593Smuzhiyun 	     (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
2368*4882a593Smuzhiyun 		speakup_register_var(var);
2369*4882a593Smuzhiyun 	for (i = 1; spk_punc_info[i].mask != 0; i++)
2370*4882a593Smuzhiyun 		spk_set_mask_bits(NULL, i, 2);
2371*4882a593Smuzhiyun 
2372*4882a593Smuzhiyun 	spk_set_key_info(spk_key_defaults, spk_key_buf);
2373*4882a593Smuzhiyun 
2374*4882a593Smuzhiyun 	/* From here on out, initializations can fail. */
2375*4882a593Smuzhiyun 	err = speakup_add_virtual_keyboard();
2376*4882a593Smuzhiyun 	if (err)
2377*4882a593Smuzhiyun 		goto error_virtkeyboard;
2378*4882a593Smuzhiyun 
2379*4882a593Smuzhiyun 	for (i = 0; i < MAX_NR_CONSOLES; i++)
2380*4882a593Smuzhiyun 		if (vc_cons[i].d) {
2381*4882a593Smuzhiyun 			err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
2382*4882a593Smuzhiyun 			if (err)
2383*4882a593Smuzhiyun 				goto error_kobjects;
2384*4882a593Smuzhiyun 		}
2385*4882a593Smuzhiyun 
2386*4882a593Smuzhiyun 	if (spk_quiet_boot)
2387*4882a593Smuzhiyun 		spk_shut_up |= 0x01;
2388*4882a593Smuzhiyun 
2389*4882a593Smuzhiyun 	err = speakup_kobj_init();
2390*4882a593Smuzhiyun 	if (err)
2391*4882a593Smuzhiyun 		goto error_kobjects;
2392*4882a593Smuzhiyun 
2393*4882a593Smuzhiyun 	spk_ttyio_register_ldisc();
2394*4882a593Smuzhiyun 	synth_init(synth_name);
2395*4882a593Smuzhiyun 	speakup_register_devsynth();
2396*4882a593Smuzhiyun 	/*
2397*4882a593Smuzhiyun 	 * register_devsynth might fail, but this error is not fatal.
2398*4882a593Smuzhiyun 	 * /dev/synth is an extra feature; the rest of Speakup
2399*4882a593Smuzhiyun 	 * will work fine without it.
2400*4882a593Smuzhiyun 	 */
2401*4882a593Smuzhiyun 
2402*4882a593Smuzhiyun 	err = register_keyboard_notifier(&keyboard_notifier_block);
2403*4882a593Smuzhiyun 	if (err)
2404*4882a593Smuzhiyun 		goto error_kbdnotifier;
2405*4882a593Smuzhiyun 	err = register_vt_notifier(&vt_notifier_block);
2406*4882a593Smuzhiyun 	if (err)
2407*4882a593Smuzhiyun 		goto error_vtnotifier;
2408*4882a593Smuzhiyun 
2409*4882a593Smuzhiyun 	speakup_task = kthread_create(speakup_thread, NULL, "speakup");
2410*4882a593Smuzhiyun 
2411*4882a593Smuzhiyun 	if (IS_ERR(speakup_task)) {
2412*4882a593Smuzhiyun 		err = PTR_ERR(speakup_task);
2413*4882a593Smuzhiyun 		goto error_task;
2414*4882a593Smuzhiyun 	}
2415*4882a593Smuzhiyun 
2416*4882a593Smuzhiyun 	set_user_nice(speakup_task, 10);
2417*4882a593Smuzhiyun 	wake_up_process(speakup_task);
2418*4882a593Smuzhiyun 
2419*4882a593Smuzhiyun 	pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2420*4882a593Smuzhiyun 	pr_info("synth name on entry is: %s\n", synth_name);
2421*4882a593Smuzhiyun 	goto out;
2422*4882a593Smuzhiyun 
2423*4882a593Smuzhiyun error_task:
2424*4882a593Smuzhiyun 	unregister_vt_notifier(&vt_notifier_block);
2425*4882a593Smuzhiyun 
2426*4882a593Smuzhiyun error_vtnotifier:
2427*4882a593Smuzhiyun 	unregister_keyboard_notifier(&keyboard_notifier_block);
2428*4882a593Smuzhiyun 	del_timer(&cursor_timer);
2429*4882a593Smuzhiyun 
2430*4882a593Smuzhiyun error_kbdnotifier:
2431*4882a593Smuzhiyun 	speakup_unregister_devsynth();
2432*4882a593Smuzhiyun 	mutex_lock(&spk_mutex);
2433*4882a593Smuzhiyun 	synth_release();
2434*4882a593Smuzhiyun 	mutex_unlock(&spk_mutex);
2435*4882a593Smuzhiyun 	speakup_kobj_exit();
2436*4882a593Smuzhiyun 
2437*4882a593Smuzhiyun error_kobjects:
2438*4882a593Smuzhiyun 	for (i = 0; i < MAX_NR_CONSOLES; i++)
2439*4882a593Smuzhiyun 		kfree(speakup_console[i]);
2440*4882a593Smuzhiyun 
2441*4882a593Smuzhiyun 	speakup_remove_virtual_keyboard();
2442*4882a593Smuzhiyun 
2443*4882a593Smuzhiyun error_virtkeyboard:
2444*4882a593Smuzhiyun 	for (i = 0; i < MAXVARS; i++)
2445*4882a593Smuzhiyun 		speakup_unregister_var(i);
2446*4882a593Smuzhiyun 
2447*4882a593Smuzhiyun 	for (i = 0; i < 256; i++) {
2448*4882a593Smuzhiyun 		if (spk_characters[i] != spk_default_chars[i])
2449*4882a593Smuzhiyun 			kfree(spk_characters[i]);
2450*4882a593Smuzhiyun 	}
2451*4882a593Smuzhiyun 
2452*4882a593Smuzhiyun 	spk_free_user_msgs();
2453*4882a593Smuzhiyun 
2454*4882a593Smuzhiyun out:
2455*4882a593Smuzhiyun 	return err;
2456*4882a593Smuzhiyun }
2457*4882a593Smuzhiyun 
2458*4882a593Smuzhiyun module_init(speakup_init);
2459*4882a593Smuzhiyun module_exit(speakup_exit);
2460