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