]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/staging/speakup/main.c
Staging: speakup: Add space around '|'
[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.
c6e3fd22
WH
19*/
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;
61module_param_named(synth, synth_name, charp, S_IRUGO);
ca2beaf8 62module_param_named(quiet, spk_quiet_boot, bool, S_IRUGO);
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);
16d35515 266 return (u_char) (scr_readw(pos) >> 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;
ca2beaf8
ST
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 }
ca2beaf8
ST
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
WH
475
476 if (w & vc->vc_hi_font_mask)
477 c |= 0x100;
478
479 ch = inverse_translate(vc, c, 0);
480 *attribs = (w & 0xff00) >> 8;
481 }
482 return ch;
483}
484
485static void say_char(struct vc_data *vc)
486{
487 u_short ch;
8e69a811 488
c6e3fd22
WH
489 spk_old_attr = spk_attr;
490 ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
491 if (spk_attr != spk_old_attr) {
ca2beaf8 492 if (spk_attrib_bleep & 1)
c6e3fd22 493 bleep(spk_y);
ca2beaf8 494 if (spk_attrib_bleep & 2)
c6e3fd22
WH
495 say_attributes(vc);
496 }
497 speak_char(ch & 0xff);
498}
499
500static void say_phonetic_char(struct vc_data *vc)
501{
502 u_short ch;
8e69a811 503
c6e3fd22
WH
504 spk_old_attr = spk_attr;
505 ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
506 if (isascii(ch) && isalpha(ch)) {
507 ch &= 0x1f;
508 synth_printf("%s\n", phonetic[--ch]);
509 } else {
510 if (IS_CHAR(ch, B_NUM))
ca2beaf8 511 synth_printf("%s ", spk_msg_get(MSG_NUMBER));
c6e3fd22
WH
512 speak_char(ch);
513 }
514}
515
516static void say_prev_char(struct vc_data *vc)
517{
518 spk_parked |= 0x01;
519 if (spk_x == 0) {
520 announce_edge(vc, edge_left);
521 return;
522 }
523 spk_x--;
524 spk_pos -= 2;
525 say_char(vc);
526}
527
528static void say_next_char(struct vc_data *vc)
529{
530 spk_parked |= 0x01;
531 if (spk_x == vc->vc_cols - 1) {
532 announce_edge(vc, edge_right);
533 return;
534 }
535 spk_x++;
536 spk_pos += 2;
537 say_char(vc);
538}
539
540/* get_word - will first check to see if the character under the
ca2beaf8
ST
541 * reading cursor is a space and if spk_say_word_ctl is true it will
542 * return the word space. If spk_say_word_ctl is not set it will check to
16d35515
WH
543 * see if there is a word starting on the next position to the right
544 * and return that word if it exists. If it does not exist it will
545 * move left to the beginning of any previous word on the line or the
13d825ed
AF
546 * beginning off the line whichever comes first..
547 */
c6e3fd22
WH
548
549static u_long get_word(struct vc_data *vc)
550{
551 u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
552 char ch;
553 u_short attr_ch;
554 u_char temp;
8e69a811 555
c6e3fd22 556 spk_old_attr = spk_attr;
16d35515 557 ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
c6e3fd22
WH
558
559/* decided to take out the sayword if on a space (mis-information */
ca2beaf8 560 if (spk_say_word_ctl && ch == SPACE) {
c6e3fd22 561 *buf = '\0';
ca2beaf8 562 synth_printf("%s\n", spk_msg_get(MSG_SPACE));
c6e3fd22
WH
563 return 0;
564 } else if ((tmpx < vc->vc_cols - 2)
565 && (ch == SPACE || ch == 0 || IS_WDLM(ch))
16d35515
WH
566 && ((char)get_char(vc, (u_short *) &tmp_pos + 1, &temp) >
567 SPACE)) {
c6e3fd22
WH
568 tmp_pos += 2;
569 tmpx++;
570 } else
571 while (tmpx > 0) {
16d35515 572 ch = (char)get_char(vc, (u_short *) tmp_pos - 1, &temp);
c6e3fd22 573 if ((ch == SPACE || ch == 0 || IS_WDLM(ch))
16d35515
WH
574 && ((char)get_char(vc, (u_short *) tmp_pos, &temp) >
575 SPACE))
c6e3fd22
WH
576 break;
577 tmp_pos -= 2;
578 tmpx--;
579 }
580 attr_ch = get_char(vc, (u_short *) tmp_pos, &spk_attr);
581 buf[cnt++] = attr_ch & 0xff;
582 while (tmpx < vc->vc_cols - 1) {
583 tmp_pos += 2;
584 tmpx++;
16d35515
WH
585 ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
586 if ((ch == SPACE) || ch == 0
587 || (IS_WDLM(buf[cnt - 1]) && (ch > SPACE)))
c6e3fd22
WH
588 break;
589 buf[cnt++] = ch;
590 }
591 buf[cnt] = '\0';
592 return cnt;
593}
594
595static void say_word(struct vc_data *vc)
596{
597 u_long cnt = get_word(vc);
ca2beaf8 598 u_short saved_punc_mask = spk_punc_mask;
8e69a811 599
c6e3fd22
WH
600 if (cnt == 0)
601 return;
ca2beaf8 602 spk_punc_mask = PUNC;
c6e3fd22
WH
603 buf[cnt++] = SPACE;
604 spkup_write(buf, cnt);
ca2beaf8 605 spk_punc_mask = saved_punc_mask;
c6e3fd22
WH
606}
607
608static void say_prev_word(struct vc_data *vc)
609{
610 u_char temp;
611 char ch;
612 u_short edge_said = 0, last_state = 0, state = 0;
8e69a811 613
c6e3fd22
WH
614 spk_parked |= 0x01;
615
616 if (spk_x == 0) {
617 if (spk_y == 0) {
618 announce_edge(vc, edge_top);
619 return;
620 }
621 spk_y--;
622 spk_x = vc->vc_cols;
623 edge_said = edge_quiet;
624 }
625 while (1) {
626 if (spk_x == 0) {
627 if (spk_y == 0) {
628 edge_said = edge_top;
629 break;
630 }
631 if (edge_said != edge_quiet)
632 edge_said = edge_left;
633 if (state > 0)
634 break;
635 spk_y--;
636 spk_x = vc->vc_cols - 1;
637 } else
638 spk_x--;
16d35515
WH
639 spk_pos -= 2;
640 ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
c6e3fd22
WH
641 if (ch == SPACE || ch == 0)
642 state = 0;
643 else if (IS_WDLM(ch))
644 state = 1;
645 else
646 state = 2;
647 if (state < last_state) {
648 spk_pos += 2;
649 spk_x++;
650 break;
651 }
652 last_state = state;
653 }
654 if (spk_x == 0 && edge_said == edge_quiet)
655 edge_said = edge_left;
656 if (edge_said > 0 && edge_said < edge_quiet)
657 announce_edge(vc, edge_said);
658 say_word(vc);
659}
660
661static void say_next_word(struct vc_data *vc)
662{
663 u_char temp;
664 char ch;
665 u_short edge_said = 0, last_state = 2, state = 0;
c6e3fd22 666
8e69a811 667 spk_parked |= 0x01;
c6e3fd22
WH
668 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
669 announce_edge(vc, edge_bottom);
670 return;
671 }
672 while (1) {
16d35515 673 ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
c6e3fd22
WH
674 if (ch == SPACE || ch == 0)
675 state = 0;
676 else if (IS_WDLM(ch))
677 state = 1;
678 else
679 state = 2;
680 if (state > last_state)
681 break;
682 if (spk_x >= vc->vc_cols - 1) {
683 if (spk_y == vc->vc_rows - 1) {
684 edge_said = edge_bottom;
685 break;
686 }
687 state = 0;
688 spk_y++;
689 spk_x = 0;
690 edge_said = edge_right;
691 } else
692 spk_x++;
693 spk_pos += 2;
694 last_state = state;
695 }
696 if (edge_said > 0)
697 announce_edge(vc, edge_said);
698 say_word(vc);
699}
700
701static void spell_word(struct vc_data *vc)
702{
0f709488 703 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
ca2beaf8
ST
704 char *cp = buf, *str_cap = spk_str_caps_stop;
705 char *cp1, *last_cap = spk_str_caps_stop;
c6e3fd22 706 u_char ch;
8e69a811 707
c6e3fd22
WH
708 if (!get_word(vc))
709 return;
710 while ((ch = (u_char) *cp)) {
711 if (cp != buf)
ca2beaf8 712 synth_printf(" %s ", delay_str[spk_spell_delay]);
c6e3fd22 713 if (IS_CHAR(ch, B_CAP)) {
ca2beaf8
ST
714 str_cap = spk_str_caps_start;
715 if (*spk_str_caps_stop)
716 spk_pitch_shift++;
16d35515 717 else /* synth has no pitch */
ca2beaf8 718 last_cap = spk_str_caps_stop;
c6e3fd22 719 } else
ca2beaf8 720 str_cap = spk_str_caps_stop;
c6e3fd22
WH
721 if (str_cap != last_cap) {
722 synth_printf("%s", str_cap);
723 last_cap = str_cap;
724 }
725 if (this_speakup_key == SPELL_PHONETIC
726 && (isascii(ch) && isalpha(ch))) {
727 ch &= 31;
728 cp1 = phonetic[--ch];
729 } else {
ca2beaf8 730 cp1 = spk_characters[ch];
c6e3fd22 731 if (*cp1 == '^') {
ca2beaf8 732 synth_printf("%s", spk_msg_get(MSG_CTRL));
c6e3fd22
WH
733 cp1++;
734 }
735 }
736 synth_printf("%s", cp1);
737 cp++;
738 }
ca2beaf8
ST
739 if (str_cap != spk_str_caps_stop)
740 synth_printf("%s", spk_str_caps_stop);
c6e3fd22
WH
741}
742
743static int get_line(struct vc_data *vc)
744{
745 u_long tmp = spk_pos - (spk_x * 2);
746 int i = 0;
747 u_char tmp2;
748
749 spk_old_attr = spk_attr;
88867e3d 750 spk_attr = get_attributes(vc, (u_short *)spk_pos);
c6e3fd22
WH
751 for (i = 0; i < vc->vc_cols; i++) {
752 buf[i] = (u_char) get_char(vc, (u_short *) tmp, &tmp2);
753 tmp += 2;
754 }
755 for (--i; i >= 0; i--)
756 if (buf[i] != SPACE)
757 break;
758 return ++i;
759}
760
761static void say_line(struct vc_data *vc)
762{
763 int i = get_line(vc);
764 char *cp;
ca2beaf8 765 u_short saved_punc_mask = spk_punc_mask;
8e69a811 766
c6e3fd22 767 if (i == 0) {
ca2beaf8 768 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
769 return;
770 }
771 buf[i++] = '\n';
772 if (this_speakup_key == SAY_LINE_INDENT) {
16d35515
WH
773 cp = buf;
774 while (*cp == SPACE)
775 cp++;
c6e3fd22
WH
776 synth_printf("%d, ", (cp - buf) + 1);
777 }
ca2beaf8 778 spk_punc_mask = spk_punc_masks[spk_reading_punc];
c6e3fd22 779 spkup_write(buf, i);
ca2beaf8 780 spk_punc_mask = saved_punc_mask;
c6e3fd22
WH
781}
782
783static void say_prev_line(struct vc_data *vc)
784{
785 spk_parked |= 0x01;
786 if (spk_y == 0) {
787 announce_edge(vc, edge_top);
788 return;
789 }
790 spk_y--;
791 spk_pos -= vc->vc_size_row;
792 say_line(vc);
793}
794
795static void say_next_line(struct vc_data *vc)
796{
797 spk_parked |= 0x01;
798 if (spk_y == vc->vc_rows - 1) {
799 announce_edge(vc, edge_bottom);
800 return;
801 }
802 spk_y++;
803 spk_pos += vc->vc_size_row;
804 say_line(vc);
805}
806
807static int say_from_to(struct vc_data *vc, u_long from, u_long to,
808 int read_punc)
809{
810 int i = 0;
811 u_char tmp;
ca2beaf8 812 u_short saved_punc_mask = spk_punc_mask;
8e69a811 813
c6e3fd22 814 spk_old_attr = spk_attr;
88867e3d 815 spk_attr = get_attributes(vc, (u_short *)from);
c6e3fd22 816 while (from < to) {
16d35515 817 buf[i++] = (char)get_char(vc, (u_short *) from, &tmp);
c6e3fd22
WH
818 from += 2;
819 if (i >= vc->vc_size_row)
820 break;
821 }
822 for (--i; i >= 0; i--)
823 if (buf[i] != SPACE)
824 break;
825 buf[++i] = SPACE;
826 buf[++i] = '\0';
827 if (i < 1)
828 return i;
829 if (read_punc)
ca2beaf8 830 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
c6e3fd22
WH
831 spkup_write(buf, i);
832 if (read_punc)
ca2beaf8 833 spk_punc_mask = saved_punc_mask;
c6e3fd22
WH
834 return i - 1;
835}
836
837static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
838 int read_punc)
839{
840 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
841 u_long end = start + (to * 2);
8e69a811 842
c6e3fd22
WH
843 start += from * 2;
844 if (say_from_to(vc, start, end, read_punc) <= 0)
845 if (cursor_track != read_all_mode)
ca2beaf8 846 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
847}
848
849/* Sentence Reading Commands */
850
c6e3fd22
WH
851static int currsentence;
852static int numsentences[2];
853static char *sentbufend[2];
854static char *sentmarks[2][10];
855static int currbuf;
856static int bn;
857static char sentbuf[2][256];
858
16d35515 859static int say_sentence_num(int num, int prev)
c6e3fd22
WH
860{
861 bn = currbuf;
862 currsentence = num + 1;
863 if (prev && --bn == -1)
864 bn = 1;
865
866 if (num > numsentences[bn])
867 return 0;
868
869 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
870 return 1;
871}
872
873static int get_sentence_buf(struct vc_data *vc, int read_punc)
874{
875 u_long start, end;
876 int i, bn;
877 u_char tmp;
878
879 currbuf++;
880 if (currbuf == 2)
881 currbuf = 0;
882 bn = currbuf;
883 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
16d35515 884 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
c6e3fd22
WH
885
886 numsentences[bn] = 0;
887 sentmarks[bn][0] = &sentbuf[bn][0];
888 i = 0;
889 spk_old_attr = spk_attr;
88867e3d 890 spk_attr = get_attributes(vc, (u_short *)start);
c6e3fd22
WH
891
892 while (start < end) {
16d35515 893 sentbuf[bn][i] = (char)get_char(vc, (u_short *) start, &tmp);
c6e3fd22 894 if (i > 0) {
16d35515 895 if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.'
c6e3fd22
WH
896 && numsentences[bn] < 9) {
897 /* Sentence Marker */
898 numsentences[bn]++;
899 sentmarks[bn][numsentences[bn]] =
16d35515 900 &sentbuf[bn][i];
c6e3fd22
WH
901 }
902 }
903 i++;
904 start += 2;
905 if (i >= vc->vc_size_row)
906 break;
907 }
908
909 for (--i; i >= 0; i--)
910 if (sentbuf[bn][i] != SPACE)
911 break;
912
913 if (i < 1)
914 return -1;
915
916 sentbuf[bn][++i] = SPACE;
917 sentbuf[bn][++i] = '\0';
918
919 sentbufend[bn] = &sentbuf[bn][i];
920 return numsentences[bn];
921}
922
923static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
924{
925 u_long start = vc->vc_origin, end;
8e69a811 926
c6e3fd22
WH
927 if (from > 0)
928 start += from * vc->vc_size_row;
929 if (to > vc->vc_rows)
930 to = vc->vc_rows;
931 end = vc->vc_origin + (to * vc->vc_size_row);
932 for (from = start; from < end; from = to) {
933 to = from + vc->vc_size_row;
934 say_from_to(vc, from, to, 1);
935 }
936}
937
938static void say_screen(struct vc_data *vc)
939{
940 say_screen_from_to(vc, 0, vc->vc_rows);
941}
942
943static void speakup_win_say(struct vc_data *vc)
944{
945 u_long start, end, from, to;
8e69a811 946
c6e3fd22 947 if (win_start < 2) {
ca2beaf8 948 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
c6e3fd22
WH
949 return;
950 }
951 start = vc->vc_origin + (win_top * vc->vc_size_row);
952 end = vc->vc_origin + (win_bottom * vc->vc_size_row);
953 while (start <= end) {
954 from = start + (win_left * 2);
955 to = start + (win_right * 2);
956 say_from_to(vc, from, to, 1);
957 start += vc->vc_size_row;
958 }
959}
960
961static void top_edge(struct vc_data *vc)
962{
963 spk_parked |= 0x01;
964 spk_pos = vc->vc_origin + 2 * spk_x;
965 spk_y = 0;
966 say_line(vc);
967}
968
969static void bottom_edge(struct vc_data *vc)
970{
971 spk_parked |= 0x01;
972 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
973 spk_y = vc->vc_rows - 1;
974 say_line(vc);
975}
976
977static void left_edge(struct vc_data *vc)
978{
979 spk_parked |= 0x01;
980 spk_pos -= spk_x * 2;
981 spk_x = 0;
982 say_char(vc);
983}
984
985static void right_edge(struct vc_data *vc)
986{
987 spk_parked |= 0x01;
988 spk_pos += (vc->vc_cols - spk_x - 1) * 2;
989 spk_x = vc->vc_cols - 1;
990 say_char(vc);
991}
992
993static void say_first_char(struct vc_data *vc)
994{
995 int i, len = get_line(vc);
996 u_char ch;
8e69a811 997
c6e3fd22
WH
998 spk_parked |= 0x01;
999 if (len == 0) {
ca2beaf8 1000 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
1001 return;
1002 }
1003 for (i = 0; i < len; i++)
1004 if (buf[i] != SPACE)
1005 break;
1006 ch = buf[i];
1007 spk_pos -= (spk_x - i) * 2;
1008 spk_x = i;
1009 synth_printf("%d, ", ++i);
1010 speak_char(ch);
1011}
1012
1013static void say_last_char(struct vc_data *vc)
1014{
1015 int len = get_line(vc);
1016 u_char ch;
8e69a811 1017
c6e3fd22
WH
1018 spk_parked |= 0x01;
1019 if (len == 0) {
ca2beaf8 1020 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
c6e3fd22
WH
1021 return;
1022 }
1023 ch = buf[--len];
1024 spk_pos -= (spk_x - len) * 2;
1025 spk_x = len;
1026 synth_printf("%d, ", ++len);
1027 speak_char(ch);
1028}
1029
1030static void say_position(struct vc_data *vc)
1031{
ca2beaf8 1032 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
16d35515 1033 vc->vc_num + 1);
c6e3fd22
WH
1034 synth_printf("\n");
1035}
1036
1037/* Added by brianb */
1038static void say_char_num(struct vc_data *vc)
1039{
1040 u_char tmp;
1041 u_short ch = get_char(vc, (u_short *) spk_pos, &tmp);
8e69a811 1042
c6e3fd22 1043 ch &= 0xff;
ca2beaf8 1044 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
c6e3fd22
WH
1045}
1046
1047/* these are stub functions to keep keyboard.c happy. */
1048
1049static void say_from_top(struct vc_data *vc)
1050{
1051 say_screen_from_to(vc, 0, spk_y);
1052}
1053
1054static void say_to_bottom(struct vc_data *vc)
1055{
1056 say_screen_from_to(vc, spk_y, vc->vc_rows);
1057}
1058
1059static void say_from_left(struct vc_data *vc)
1060{
1061 say_line_from_to(vc, 0, spk_x, 1);
1062}
1063
1064static void say_to_right(struct vc_data *vc)
1065{
1066 say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1067}
1068
1069/* end of stub functions. */
1070
1071static void spkup_write(const char *in_buf, int count)
1072{
16d35515 1073 static int rep_count;
c6e3fd22 1074 static u_char ch = '\0', old_ch = '\0';
16d35515 1075 static u_short char_type, last_type;
c6e3fd22 1076 int in_count = count;
8e69a811 1077
c6e3fd22
WH
1078 spk_keydown = 0;
1079 while (count--) {
1080 if (cursor_track == read_all_mode) {
1081 /* Insert Sentence Index */
1082 if ((in_buf == sentmarks[bn][currsentence]) &&
16d35515 1083 (currsentence <= numsentences[bn]))
c6e3fd22
WH
1084 synth_insert_next_index(currsentence++);
1085 }
16d35515 1086 ch = (u_char) *in_buf++;
c6e3fd22 1087 char_type = spk_chartab[ch];
16d35515 1088 if (ch == old_ch && !(char_type & B_NUM)) {
c6e3fd22
WH
1089 if (++rep_count > 2)
1090 continue;
1091 } else {
16d35515 1092 if ((last_type & CH_RPT) && rep_count > 2) {
c6e3fd22 1093 synth_printf(" ");
ca2beaf8 1094 synth_printf(spk_msg_get(MSG_REPEAT_DESC),
16d35515 1095 ++rep_count);
c6e3fd22
WH
1096 synth_printf(" ");
1097 }
1098 rep_count = 0;
1099 }
1100 if (ch == spk_lastkey) {
1101 rep_count = 0;
ca2beaf8 1102 if (spk_key_echo == 1 && ch >= MINECHOCHAR)
c6e3fd22
WH
1103 speak_char(ch);
1104 } else if (char_type & B_ALPHA) {
1105 if ((synth_flags & SF_DEC) && (last_type & PUNC))
1106 synth_buffer_add(SPACE);
1107 synth_printf("%c", ch);
1108 } else if (char_type & B_NUM) {
1109 rep_count = 0;
1110 synth_printf("%c", ch);
ca2beaf8 1111 } else if (char_type & spk_punc_mask) {
c6e3fd22 1112 speak_char(ch);
16d35515
WH
1113 char_type &= ~PUNC; /* for dec nospell processing */
1114 } else if (char_type & SYNTH_OK) {
1115 /* these are usually puncts like . and , which synth
1116 * needs for expression.
1117 * suppress multiple to get rid of long pauses and
1118 * clear repeat count
1119 * so if someone has
13d825ed
AF
1120 * repeats on you don't get nothing repeated count
1121 */
c6e3fd22
WH
1122 if (ch != old_ch)
1123 synth_printf("%c", ch);
1124 else
1125 rep_count = 0;
1126 } else {
1127/* send space and record position, if next is num overwrite space */
1128 if (old_ch != ch)
1129 synth_buffer_add(SPACE);
1130 else
1131 rep_count = 0;
1132 }
1133 old_ch = ch;
1134 last_type = char_type;
1135 }
1136 spk_lastkey = 0;
1137 if (in_count > 2 && rep_count > 2) {
16d35515 1138 if (last_type & CH_RPT) {
c6e3fd22 1139 synth_printf(" ");
63b8ebe4
SG
1140 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1141 ++rep_count);
c6e3fd22
WH
1142 synth_printf(" ");
1143 }
1144 rep_count = 0;
1145 }
1146}
1147
1148static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1149
1150static void read_all_doc(struct vc_data *vc);
1151static void cursor_done(u_long data);
1152static DEFINE_TIMER(cursor_timer, cursor_done, 0, 0);
1153
1154static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1155{
1156 unsigned long flags;
8e69a811 1157
c6e3fd22
WH
1158 if (synth == NULL || up_flag || spk_killed)
1159 return;
3efe810f 1160 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1161 if (cursor_track == read_all_mode) {
1162 switch (value) {
1163 case KVAL(K_SHIFT):
1164 del_timer(&cursor_timer);
1165 spk_shut_up &= 0xfe;
ca2beaf8 1166 spk_do_flush();
c6e3fd22
WH
1167 read_all_doc(vc);
1168 break;
1169 case KVAL(K_CTRL):
1170 del_timer(&cursor_timer);
1171 cursor_track = prev_cursor_track;
1172 spk_shut_up &= 0xfe;
ca2beaf8 1173 spk_do_flush();
c6e3fd22
WH
1174 break;
1175 }
1176 } else {
1177 spk_shut_up &= 0xfe;
ca2beaf8 1178 spk_do_flush();
c6e3fd22 1179 }
ca2beaf8
ST
1180 if (spk_say_ctrl && value < NUM_CTL_LABELS)
1181 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
3efe810f 1182 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1183}
1184
1185static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1186{
1187 unsigned long flags;
8e69a811 1188
3efe810f 1189 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1190 if (up_flag) {
1191 spk_lastkey = spk_keydown = 0;
3efe810f 1192 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1193 return;
1194 }
1195 if (synth == NULL || spk_killed) {
3efe810f 1196 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1197 return;
1198 }
1199 spk_shut_up &= 0xfe;
1200 spk_lastkey = value;
1201 spk_keydown++;
1202 spk_parked &= 0xfe;
ca2beaf8 1203 if (spk_key_echo == 2 && value >= MINECHOCHAR)
c6e3fd22 1204 speak_char(value);
3efe810f 1205 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1206}
1207
ca2beaf8 1208int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
c6e3fd22
WH
1209{
1210 int i = 0, states, key_data_len;
1211 const u_char *cp = key_info;
1212 u_char *cp1 = k_buffer;
1213 u_char ch, version, num_keys;
8e69a811 1214
c6e3fd22
WH
1215 version = *cp++;
1216 if (version != KEY_MAP_VER)
1217 return -1;
1218 num_keys = *cp;
16d35515 1219 states = (int)cp[1];
c6e3fd22 1220 key_data_len = (states + 1) * (num_keys + 1);
ca2beaf8 1221 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf))
c6e3fd22
WH
1222 return -2;
1223 memset(k_buffer, 0, SHIFT_TBL_SIZE);
ca2beaf8
ST
1224 memset(spk_our_keys, 0, sizeof(spk_our_keys));
1225 spk_shift_table = k_buffer;
1226 spk_our_keys[0] = spk_shift_table;
c6e3fd22
WH
1227 cp1 += SHIFT_TBL_SIZE;
1228 memcpy(cp1, cp, key_data_len + 3);
16d35515
WH
1229 /* get num_keys, states and data */
1230 cp1 += 2; /* now pointing at shift states */
c6e3fd22
WH
1231 for (i = 1; i <= states; i++) {
1232 ch = *cp1++;
1233 if (ch >= SHIFT_TBL_SIZE)
1234 return -3;
ca2beaf8 1235 spk_shift_table[ch] = i;
c6e3fd22
WH
1236 }
1237 keymap_flags = *cp1++;
1238 while ((ch = *cp1)) {
1239 if (ch >= MAX_KEY)
1240 return -4;
ca2beaf8 1241 spk_our_keys[ch] = cp1;
c6e3fd22
WH
1242 cp1 += states + 1;
1243 }
1244 return 0;
1245}
1246
1247static struct var_t spk_vars[] = {
1248 /* bell must be first to set high limit */
16d35515
WH
1249 {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
1250 {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
1251 {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
1252 {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
1253 {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
1254 {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1255 {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1256 {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
1257 {SAY_CONTROL, TOGGLE_0},
1258 {SAY_WORD_CTL, TOGGLE_0},
1259 {NO_INTERRUPT, TOGGLE_0},
1260 {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
c6e3fd22
WH
1261 V_LAST_VAR
1262};
1263
c6e3fd22
WH
1264static void toggle_cursoring(struct vc_data *vc)
1265{
1266 if (cursor_track == read_all_mode)
1267 cursor_track = prev_cursor_track;
1268 if (++cursor_track >= CT_Max)
1269 cursor_track = 0;
ca2beaf8 1270 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
c6e3fd22
WH
1271}
1272
ca2beaf8 1273void spk_reset_default_chars(void)
c6e3fd22
WH
1274{
1275 int i;
1276
1277 /* First, free any non-default */
1278 for (i = 0; i < 256; i++) {
ca2beaf8
ST
1279 if ((spk_characters[i] != NULL)
1280 && (spk_characters[i] != spk_default_chars[i]))
1281 kfree(spk_characters[i]);
c6e3fd22
WH
1282 }
1283
ca2beaf8 1284 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
c6e3fd22
WH
1285}
1286
ca2beaf8 1287void spk_reset_default_chartab(void)
c6e3fd22
WH
1288{
1289 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1290}
1291
16d35515 1292static const struct st_bits_data *pb_edit;
c6e3fd22
WH
1293
1294static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1295{
1296 short mask = pb_edit->mask, ch_type = spk_chartab[ch];
8e69a811 1297
16d35515 1298 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
c6e3fd22
WH
1299 return -1;
1300 if (ch == SPACE) {
ca2beaf8
ST
1301 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1302 spk_special_handler = NULL;
c6e3fd22
WH
1303 return 1;
1304 }
16d35515 1305 if (mask < PUNC && !(ch_type & PUNC))
c6e3fd22
WH
1306 return -1;
1307 spk_chartab[ch] ^= mask;
1308 speak_char(ch);
1309 synth_printf(" %s\n",
ca2beaf8
ST
1310 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1311 spk_msg_get(MSG_OFF));
c6e3fd22
WH
1312 return 1;
1313}
1314
1315/* Allocation concurrency is protected by the console semaphore */
0012196c 1316static int speakup_allocate(struct vc_data *vc)
c6e3fd22
WH
1317{
1318 int vc_num;
1319
1320 vc_num = vc->vc_num;
1321 if (speakup_console[vc_num] == NULL) {
1322 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
16d35515 1323 GFP_ATOMIC);
c6e3fd22 1324 if (speakup_console[vc_num] == NULL)
628f3428 1325 return -ENOMEM;
c6e3fd22
WH
1326 speakup_date(vc);
1327 } else if (!spk_parked)
1328 speakup_date(vc);
628f3428
CB
1329
1330 return 0;
c6e3fd22
WH
1331}
1332
0012196c 1333static void speakup_deallocate(struct vc_data *vc)
c6e3fd22
WH
1334{
1335 int vc_num;
1336
1337 vc_num = vc->vc_num;
39dd3e5d
IM
1338 kfree(speakup_console[vc_num]);
1339 speakup_console[vc_num] = NULL;
c6e3fd22
WH
1340}
1341
1342static u_char is_cursor;
1343static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1344static int cursor_con;
1345
1346static void reset_highlight_buffers(struct vc_data *);
1347
1348static int read_all_key;
1349
c6e3fd22
WH
1350static void start_read_all_timer(struct vc_data *vc, int command);
1351
1352enum {
1353 RA_NOTHING,
1354 RA_NEXT_SENT,
1355 RA_PREV_LINE,
1356 RA_NEXT_LINE,
1357 RA_PREV_SENT,
1358 RA_DOWN_ARROW,
1359 RA_TIMER,
1360 RA_FIND_NEXT_SENT,
1361 RA_FIND_PREV_SENT,
1362};
1363
16d35515 1364static void kbd_fakekey2(struct vc_data *vc, int command)
c6e3fd22
WH
1365{
1366 del_timer(&cursor_timer);
1367 speakup_fake_down_arrow();
1368 start_read_all_timer(vc, command);
1369}
1370
16d35515 1371static void read_all_doc(struct vc_data *vc)
c6e3fd22
WH
1372{
1373 if ((vc->vc_num != fg_console) || synth == NULL || spk_shut_up)
1374 return;
1375 if (!synth_supports_indexing())
1376 return;
1377 if (cursor_track != read_all_mode)
1378 prev_cursor_track = cursor_track;
1379 cursor_track = read_all_mode;
ca2beaf8 1380 spk_reset_index_count(0);
c6e3fd22
WH
1381 if (get_sentence_buf(vc, 0) == -1)
1382 kbd_fakekey2(vc, RA_DOWN_ARROW);
1383 else {
1384 say_sentence_num(0, 0);
1385 synth_insert_next_index(0);
1386 start_read_all_timer(vc, RA_TIMER);
1387 }
1388}
1389
16d35515 1390static void stop_read_all(struct vc_data *vc)
c6e3fd22
WH
1391{
1392 del_timer(&cursor_timer);
1393 cursor_track = prev_cursor_track;
1394 spk_shut_up &= 0xfe;
ca2beaf8 1395 spk_do_flush();
c6e3fd22
WH
1396}
1397
16d35515 1398static void start_read_all_timer(struct vc_data *vc, int command)
c6e3fd22
WH
1399{
1400 struct var_t *cursor_timeout;
1401
1402 cursor_con = vc->vc_num;
1403 read_all_key = command;
ca2beaf8 1404 cursor_timeout = spk_get_var(CURSOR_TIME);
16d35515
WH
1405 mod_timer(&cursor_timer,
1406 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
c6e3fd22
WH
1407}
1408
16d35515 1409static void handle_cursor_read_all(struct vc_data *vc, int command)
c6e3fd22
WH
1410{
1411 int indcount, sentcount, rv, sn;
1412
1413 switch (command) {
1414 case RA_NEXT_SENT:
1415 /* Get Current Sentence */
ca2beaf8 1416 spk_get_index_count(&indcount, &sentcount);
c6e3fd22 1417 /*printk("%d %d ", indcount, sentcount); */
ca2beaf8 1418 spk_reset_index_count(sentcount + 1);
c6e3fd22 1419 if (indcount == 1) {
16d35515 1420 if (!say_sentence_num(sentcount + 1, 0)) {
c6e3fd22
WH
1421 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1422 return;
1423 }
1424 synth_insert_next_index(0);
1425 } else {
1426 sn = 0;
16d35515 1427 if (!say_sentence_num(sentcount + 1, 1)) {
c6e3fd22 1428 sn = 1;
ca2beaf8 1429 spk_reset_index_count(sn);
c6e3fd22
WH
1430 } else
1431 synth_insert_next_index(0);
1432 if (!say_sentence_num(sn, 0)) {
1433 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1434 return;
1435 }
1436 synth_insert_next_index(0);
1437 }
1438 start_read_all_timer(vc, RA_TIMER);
1439 break;
1440 case RA_PREV_SENT:
1441 break;
1442 case RA_NEXT_LINE:
1443 read_all_doc(vc);
1444 break;
1445 case RA_PREV_LINE:
1446 break;
1447 case RA_DOWN_ARROW:
1448 if (get_sentence_buf(vc, 0) == -1) {
1449 kbd_fakekey2(vc, RA_DOWN_ARROW);
1450 } else {
1451 say_sentence_num(0, 0);
1452 synth_insert_next_index(0);
1453 start_read_all_timer(vc, RA_TIMER);
1454 }
1455 break;
1456 case RA_FIND_NEXT_SENT:
1457 rv = get_sentence_buf(vc, 0);
1458 if (rv == -1)
1459 read_all_doc(vc);
1460 if (rv == 0)
1461 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1462 else {
1463 say_sentence_num(1, 0);
1464 synth_insert_next_index(0);
1465 start_read_all_timer(vc, RA_TIMER);
1466 }
1467 break;
1468 case RA_FIND_PREV_SENT:
1469 break;
1470 case RA_TIMER:
ca2beaf8 1471 spk_get_index_count(&indcount, &sentcount);
c6e3fd22
WH
1472 if (indcount < 2)
1473 kbd_fakekey2(vc, RA_DOWN_ARROW);
1474 else
1475 start_read_all_timer(vc, RA_TIMER);
1476 break;
1477 }
1478}
1479
1480static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1481{
1482 unsigned long flags;
8e69a811 1483
3efe810f 1484 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1485 if (cursor_track == read_all_mode) {
1486 spk_parked &= 0xfe;
1487 if (synth == NULL || up_flag || spk_shut_up) {
3efe810f 1488 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1489 return NOTIFY_STOP;
1490 }
1491 del_timer(&cursor_timer);
1492 spk_shut_up &= 0xfe;
ca2beaf8 1493 spk_do_flush();
16d35515 1494 start_read_all_timer(vc, value + 1);
3efe810f 1495 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1496 return NOTIFY_STOP;
1497 }
3efe810f 1498 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1499 return NOTIFY_OK;
1500}
1501
1502static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1503{
1504 unsigned long flags;
1505 struct var_t *cursor_timeout;
1506
3efe810f 1507 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1508 spk_parked &= 0xfe;
1509 if (synth == NULL || up_flag || spk_shut_up || cursor_track == CT_Off) {
3efe810f 1510 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1511 return;
1512 }
1513 spk_shut_up &= 0xfe;
ca2beaf8
ST
1514 if (spk_no_intr)
1515 spk_do_flush();
c6e3fd22 1516/* the key press flushes if !no_inter but we want to flush on cursor
13d825ed
AF
1517 * moves regardless of no_inter state
1518 */
c6e3fd22
WH
1519 is_cursor = value + 1;
1520 old_cursor_pos = vc->vc_pos;
1521 old_cursor_x = vc->vc_x;
1522 old_cursor_y = vc->vc_y;
1523 speakup_console[vc->vc_num]->ht.cy = vc->vc_y;
1524 cursor_con = vc->vc_num;
1525 if (cursor_track == CT_Highlight)
1526 reset_highlight_buffers(vc);
ca2beaf8 1527 cursor_timeout = spk_get_var(CURSOR_TIME);
16d35515
WH
1528 mod_timer(&cursor_timer,
1529 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
3efe810f 1530 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1531}
1532
16d35515 1533static void update_color_buffer(struct vc_data *vc, const char *ic, int len)
c6e3fd22
WH
1534{
1535 int i, bi, hi;
1536 int vc_num = vc->vc_num;
1537
6de3f58b 1538 bi = (vc->vc_attr & 0x70) >> 4;
c6e3fd22
WH
1539 hi = speakup_console[vc_num]->ht.highsize[bi];
1540
1541 i = 0;
1542 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1543 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1544 speakup_console[vc_num]->ht.rx[bi] = vc->vc_x;
1545 speakup_console[vc_num]->ht.ry[bi] = vc->vc_y;
1546 }
1547 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1548 if ((ic[i] > 32) && (ic[i] < 127)) {
1549 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1550 hi++;
1551 } else if ((ic[i] == 32) && (hi != 0)) {
16d35515
WH
1552 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1553 32) {
c6e3fd22 1554 speakup_console[vc_num]->ht.highbuf[bi][hi] =
16d35515 1555 ic[i];
c6e3fd22
WH
1556 hi++;
1557 }
1558 }
1559 i++;
1560 }
1561 speakup_console[vc_num]->ht.highsize[bi] = hi;
1562}
1563
16d35515 1564static void reset_highlight_buffers(struct vc_data *vc)
c6e3fd22
WH
1565{
1566 int i;
1567 int vc_num = vc->vc_num;
8e69a811 1568
16d35515 1569 for (i = 0; i < 8; i++)
c6e3fd22
WH
1570 speakup_console[vc_num]->ht.highsize[i] = 0;
1571}
1572
16d35515 1573static int count_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1574{
1575 int i, bg;
1576 int cc;
1577 int vc_num = vc->vc_num;
1578 u16 ch;
1579 u16 *start = (u16 *) vc->vc_origin;
1580
1581 for (i = 0; i < 8; i++)
1582 speakup_console[vc_num]->ht.bgcount[i] = 0;
1583
1584 for (i = 0; i < vc->vc_rows; i++) {
16d35515 1585 u16 *end = start + vc->vc_cols * 2;
c6e3fd22 1586 u16 *ptr;
8e69a811 1587
c6e3fd22 1588 for (ptr = start; ptr < end; ptr++) {
88867e3d 1589 ch = get_attributes(vc, ptr);
c6e3fd22
WH
1590 bg = (ch & 0x70) >> 4;
1591 speakup_console[vc_num]->ht.bgcount[bg]++;
1592 }
1593 start += vc->vc_size_row;
1594 }
1595
1596 cc = 0;
1597 for (i = 0; i < 8; i++)
1598 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1599 cc++;
1600 return cc;
1601}
1602
16d35515 1603static int get_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1604{
1605 int i, j;
590aeb17 1606 unsigned int cptr[8];
c6e3fd22
WH
1607 int vc_num = vc->vc_num;
1608
1609 for (i = 0; i < 8; i++)
1610 cptr[i] = i;
1611
1612 for (i = 0; i < 7; i++)
1613 for (j = i + 1; j < 8; j++)
1614 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
590aeb17
FF
1615 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1616 swap(cptr[i], cptr[j]);
c6e3fd22
WH
1617
1618 for (i = 0; i < 8; i++)
1619 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1620 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1621 return cptr[i];
1622 return -1;
1623}
1624
16d35515 1625static int speak_highlight(struct vc_data *vc)
c6e3fd22
WH
1626{
1627 int hc, d;
1628 int vc_num = vc->vc_num;
8e69a811 1629
c6e3fd22
WH
1630 if (count_highlight_color(vc) == 1)
1631 return 0;
1632 hc = get_highlight_color(vc);
1633 if (hc != -1) {
16d35515 1634 d = vc->vc_y - speakup_console[vc_num]->ht.cy;
c6e3fd22
WH
1635 if ((d == 1) || (d == -1))
1636 if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y)
1637 return 0;
1638 spk_parked |= 0x01;
ca2beaf8 1639 spk_do_flush();
c6e3fd22 1640 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
16d35515 1641 speakup_console[vc_num]->ht.highsize[hc]);
c6e3fd22
WH
1642 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1643 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1644 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1645 return 1;
1646 }
1647 return 0;
1648}
1649
16d35515 1650static void cursor_done(u_long data)
c6e3fd22
WH
1651{
1652 struct vc_data *vc = vc_cons[cursor_con].d;
1653 unsigned long flags;
8e69a811 1654
c6e3fd22 1655 del_timer(&cursor_timer);
3efe810f 1656 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1657 if (cursor_con != fg_console) {
1658 is_cursor = 0;
1659 goto out;
1660 }
1661 speakup_date(vc);
1662 if (win_enabled) {
1663 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1664 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
c6e3fd22
WH
1665 spk_keydown = is_cursor = 0;
1666 goto out;
1667 }
1668 }
1669 if (cursor_track == read_all_mode) {
1670 handle_cursor_read_all(vc, read_all_key);
1671 goto out;
1672 }
1673 if (cursor_track == CT_Highlight) {
1674 if (speak_highlight(vc)) {
1675 spk_keydown = is_cursor = 0;
1676 goto out;
1677 }
1678 }
1679 if (cursor_track == CT_Window)
1680 speakup_win_say(vc);
1681 else if (is_cursor == 1 || is_cursor == 4)
1682 say_line_from_to(vc, 0, vc->vc_cols, 0);
1683 else
1684 say_char(vc);
1685 spk_keydown = is_cursor = 0;
1686out:
3efe810f 1687 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1688}
1689
1690/* called by: vt_notifier_call() */
1691static void speakup_bs(struct vc_data *vc)
1692{
1693 unsigned long flags;
8e69a811 1694
c6e3fd22
WH
1695 if (!speakup_console[vc->vc_num])
1696 return;
3efe810f 1697 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1698 /* Speakup output, discard */
1699 return;
1700 if (!spk_parked)
1701 speakup_date(vc);
1702 if (spk_shut_up || synth == NULL) {
3efe810f 1703 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1704 return;
1705 }
1706 if (vc->vc_num == fg_console && spk_keydown) {
1707 spk_keydown = 0;
1708 if (!is_cursor)
1709 say_char(vc);
1710 }
3efe810f 1711 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1712}
1713
1714/* called by: vt_notifier_call() */
1715static void speakup_con_write(struct vc_data *vc, const char *str, int len)
1716{
1717 unsigned long flags;
8e69a811 1718
c6e3fd22
WH
1719 if ((vc->vc_num != fg_console) || spk_shut_up || synth == NULL)
1720 return;
3efe810f 1721 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1722 /* Speakup output, discard */
1723 return;
ca2beaf8 1724 if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1))
c6e3fd22
WH
1725 bleep(3);
1726 if ((is_cursor) || (cursor_track == read_all_mode)) {
1727 if (cursor_track == CT_Highlight)
1728 update_color_buffer(vc, str, len);
3efe810f 1729 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1730 return;
1731 }
1732 if (win_enabled) {
1733 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1734 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
3efe810f 1735 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1736 return;
1737 }
1738 }
1739
1740 spkup_write(str, len);
3efe810f 1741 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1742}
1743
0012196c 1744static void speakup_con_update(struct vc_data *vc)
c6e3fd22
WH
1745{
1746 unsigned long flags;
8e69a811 1747
c6e3fd22
WH
1748 if (speakup_console[vc->vc_num] == NULL || spk_parked)
1749 return;
3efe810f 1750 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1751 /* Speakup output, discard */
1752 return;
1753 speakup_date(vc);
3efe810f 1754 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1755}
1756
1757static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1758{
1759 unsigned long flags;
1760 int on_off = 2;
1761 char *label;
8e69a811 1762
c6e3fd22
WH
1763 if (synth == NULL || up_flag || spk_killed)
1764 return;
3efe810f 1765 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22 1766 spk_shut_up &= 0xfe;
ca2beaf8
ST
1767 if (spk_no_intr)
1768 spk_do_flush();
c6e3fd22
WH
1769 switch (value) {
1770 case KVAL(K_CAPS):
ca2beaf8 1771 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
079c9534 1772 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
c6e3fd22
WH
1773 break;
1774 case KVAL(K_NUM):
ca2beaf8 1775 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
079c9534 1776 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
c6e3fd22
WH
1777 break;
1778 case KVAL(K_HOLD):
ca2beaf8 1779 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
079c9534 1780 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
c6e3fd22
WH
1781 if (speakup_console[vc->vc_num])
1782 speakup_console[vc->vc_num]->tty_stopped = on_off;
1783 break;
1784 default:
1785 spk_parked &= 0xfe;
3efe810f 1786 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1787 return;
1788 }
1789 if (on_off < 2)
1790 synth_printf("%s %s\n",
ca2beaf8 1791 label, spk_msg_get(MSG_STATUS_START + on_off));
3efe810f 1792 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1793}
1794
16d35515 1795static int inc_dec_var(u_char value)
c6e3fd22
WH
1796{
1797 struct st_var_header *p_header;
1798 struct var_t *var_data;
1799 char num_buf[32];
1800 char *cp = num_buf;
1801 char *pn;
1802 int var_id = (int)value - VAR_START;
16d35515 1803 int how = (var_id & 1) ? E_INC : E_DEC;
8e69a811 1804
16d35515 1805 var_id = var_id / 2 + FIRST_SET_VAR;
ca2beaf8 1806 p_header = spk_get_var_header(var_id);
c6e3fd22
WH
1807 if (p_header == NULL)
1808 return -1;
1809 if (p_header->var_type != VAR_NUM)
1810 return -1;
1811 var_data = p_header->data;
ca2beaf8 1812 if (spk_set_num_var(1, p_header, how) != 0)
c6e3fd22
WH
1813 return -1;
1814 if (!spk_close_press) {
1815 for (pn = p_header->name; *pn; pn++) {
1816 if (*pn == '_')
1817 *cp = SPACE;
1818 else
1819 *cp++ = *pn;
1820 }
1821 }
1822 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
16d35515 1823 var_data->u.n.value);
c6e3fd22
WH
1824 synth_printf("%s", num_buf);
1825 return 0;
1826}
1827
16d35515 1828static void speakup_win_set(struct vc_data *vc)
c6e3fd22
WH
1829{
1830 char info[40];
8e69a811 1831
c6e3fd22 1832 if (win_start > 1) {
ca2beaf8 1833 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
c6e3fd22
WH
1834 return;
1835 }
1836 if (spk_x < win_left || spk_y < win_top) {
ca2beaf8 1837 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
c6e3fd22
WH
1838 return;
1839 }
1840 if (win_start && spk_x == win_left && spk_y == win_top) {
1841 win_left = 0;
16d35515 1842 win_right = vc->vc_cols - 1;
c6e3fd22 1843 win_bottom = spk_y;
ca2beaf8 1844 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
16d35515 1845 (int)win_top + 1);
c6e3fd22
WH
1846 } else {
1847 if (!win_start) {
1848 win_top = spk_y;
1849 win_left = spk_x;
1850 } else {
1851 win_bottom = spk_y;
1852 win_right = spk_x;
1853 }
ca2beaf8 1854 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
63b8ebe4
SG
1855 (win_start) ?
1856 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
16d35515 1857 (int)spk_y + 1, (int)spk_x + 1);
c6e3fd22
WH
1858 }
1859 synth_printf("%s\n", info);
1860 win_start++;
1861}
1862
16d35515 1863static void speakup_win_clear(struct vc_data *vc)
c6e3fd22
WH
1864{
1865 win_top = win_bottom = 0;
1866 win_left = win_right = 0;
1867 win_start = 0;
ca2beaf8 1868 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
c6e3fd22
WH
1869}
1870
16d35515 1871static void speakup_win_enable(struct vc_data *vc)
c6e3fd22
WH
1872{
1873 if (win_start < 2) {
ca2beaf8 1874 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
c6e3fd22
WH
1875 return;
1876 }
1877 win_enabled ^= 1;
1878 if (win_enabled)
ca2beaf8 1879 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
c6e3fd22 1880 else
ca2beaf8 1881 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
c6e3fd22
WH
1882}
1883
16d35515 1884static void speakup_bits(struct vc_data *vc)
c6e3fd22
WH
1885{
1886 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
8e69a811 1887
ca2beaf8
ST
1888 if (spk_special_handler != NULL || val < 1 || val > 6) {
1889 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
c6e3fd22
WH
1890 return;
1891 }
ca2beaf8
ST
1892 pb_edit = &spk_punc_info[val];
1893 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1894 spk_special_handler = edit_bits;
c6e3fd22
WH
1895}
1896
1897static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1898{
4ea418b8 1899 static u_char goto_buf[8];
16d35515 1900 static int num;
ef35a4f4 1901 int maxlen;
c6e3fd22 1902 char *cp;
ef35a4f4 1903
c6e3fd22
WH
1904 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1905 goto do_goto;
1906 if (type == KT_LATIN && ch == '\n')
1907 goto do_goto;
1908 if (type != 0)
1909 goto oops;
1910 if (ch == 8) {
1911 if (num == 0)
1912 return -1;
1913 ch = goto_buf[--num];
1914 goto_buf[num] = '\0';
1915 spkup_write(&ch, 1);
1916 return 1;
1917 }
1918 if (ch < '+' || ch > 'y')
1919 goto oops;
1920 goto_buf[num++] = ch;
1921 goto_buf[num] = '\0';
1922 spkup_write(&ch, 1);
1923 maxlen = (*goto_buf >= '0') ? 3 : 4;
1924 if ((ch == '+' || ch == '-') && num == 1)
1925 return 1;
1926 if (ch >= '0' && ch <= '9' && num < maxlen)
1927 return 1;
16d35515 1928 if (num < maxlen - 1 || num > maxlen)
c6e3fd22
WH
1929 goto oops;
1930 if (ch < 'x' || ch > 'y') {
1931oops:
1932 if (!spk_killed)
ca2beaf8 1933 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
c6e3fd22 1934 goto_buf[num = 0] = '\0';
ca2beaf8 1935 spk_special_handler = NULL;
c6e3fd22
WH
1936 return 1;
1937 }
ef35a4f4
DY
1938
1939 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1940
c6e3fd22
WH
1941 if (*cp == 'x') {
1942 if (*goto_buf < '0')
1943 goto_pos += spk_x;
ef35a4f4 1944 else if (goto_pos > 0)
c6e3fd22 1945 goto_pos--;
ef35a4f4 1946
c6e3fd22 1947 if (goto_pos >= vc->vc_cols)
16d35515 1948 goto_pos = vc->vc_cols - 1;
c6e3fd22
WH
1949 goto_x = 1;
1950 } else {
1951 if (*goto_buf < '0')
1952 goto_pos += spk_y;
ef35a4f4 1953 else if (goto_pos > 0)
c6e3fd22 1954 goto_pos--;
ef35a4f4 1955
c6e3fd22 1956 if (goto_pos >= vc->vc_rows)
16d35515 1957 goto_pos = vc->vc_rows - 1;
c6e3fd22
WH
1958 goto_x = 0;
1959 }
16d35515 1960 goto_buf[num = 0] = '\0';
c6e3fd22 1961do_goto:
ca2beaf8 1962 spk_special_handler = NULL;
c6e3fd22
WH
1963 spk_parked |= 0x01;
1964 if (goto_x) {
1965 spk_pos -= spk_x * 2;
1966 spk_x = goto_pos;
1967 spk_pos += goto_pos * 2;
1968 say_word(vc);
1969 } else {
1970 spk_y = goto_pos;
1971 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
1972 say_line(vc);
1973 }
1974 return 1;
1975}
1976
16d35515 1977static void speakup_goto(struct vc_data *vc)
c6e3fd22 1978{
ca2beaf8
ST
1979 if (spk_special_handler != NULL) {
1980 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
c6e3fd22
WH
1981 return;
1982 }
ca2beaf8
ST
1983 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
1984 spk_special_handler = handle_goto;
c6e3fd22
WH
1985}
1986
1987static void speakup_help(struct vc_data *vc)
1988{
ca2beaf8 1989 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
c6e3fd22
WH
1990}
1991
16d35515 1992static void do_nothing(struct vc_data *vc)
c6e3fd22 1993{
16d35515 1994 return; /* flush done in do_spkup */
c6e3fd22 1995}
16d35515 1996
c6e3fd22
WH
1997static u_char key_speakup, spk_key_locked;
1998
16d35515 1999static void speakup_lock(struct vc_data *vc)
c6e3fd22
WH
2000{
2001 if (!spk_key_locked)
2002 spk_key_locked = key_speakup = 16;
2003 else
2004 spk_key_locked = key_speakup = 0;
2005}
2006
16d35515 2007typedef void (*spkup_hand) (struct vc_data *);
0012196c 2008static spkup_hand spkup_handler[] = {
c6e3fd22
WH
2009 /* must be ordered same as defines in speakup.h */
2010 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2011 speakup_cut, speakup_paste, say_first_char, say_last_char,
2012 say_char, say_prev_char, say_next_char,
2013 say_word, say_prev_word, say_next_word,
2014 say_line, say_prev_line, say_next_line,
2015 top_edge, bottom_edge, left_edge, right_edge,
2016 spell_word, spell_word, say_screen,
2017 say_position, say_attributes,
16d35515 2018 speakup_off, speakup_parked, say_line, /* this is for indent */
c6e3fd22
WH
2019 say_from_top, say_to_bottom,
2020 say_from_left, say_to_right,
2021 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2022 speakup_bits, speakup_bits, speakup_bits,
2023 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2024 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2025};
2026
2027static void do_spkup(struct vc_data *vc, u_char value)
2028{
2029 if (spk_killed && value != SPEECH_KILL)
2030 return;
2031 spk_keydown = 0;
2032 spk_lastkey = 0;
2033 spk_shut_up &= 0xfe;
2034 this_speakup_key = value;
2035 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
ca2beaf8 2036 spk_do_flush();
16d35515 2037 (*spkup_handler[value]) (vc);
c6e3fd22
WH
2038 } else {
2039 if (inc_dec_var(value) < 0)
2040 bleep(9);
2041 }
2042}
2043
2044static const char *pad_chars = "0123456789+-*/\015,.?()";
2045
0012196c 2046static int
c6e3fd22 2047speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
16d35515 2048 int up_flag)
c6e3fd22
WH
2049{
2050 unsigned long flags;
2051 int kh;
2052 u_char *key_info;
2053 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2054 u_char shift_info, offset;
2055 int ret = 0;
8e69a811 2056
c6e3fd22
WH
2057 if (synth == NULL)
2058 return 0;
2059
3efe810f 2060 spin_lock_irqsave(&speakup_info.spinlock, flags);
5b19208a 2061 tty = vc->port.tty;
c6e3fd22
WH
2062 if (type >= 0xf0)
2063 type -= 0xf0;
16d35515 2064 if (type == KT_PAD
079c9534 2065 && (vt_get_leds(fg_console, VC_NUMLOCK))) {
c6e3fd22
WH
2066 if (up_flag) {
2067 spk_keydown = 0;
2068 goto out;
2069 }
2070 value = spk_lastkey = pad_chars[value];
2071 spk_keydown++;
2072 spk_parked &= 0xfe;
2073 goto no_map;
2074 }
2075 if (keycode >= MAX_KEY)
2076 goto no_map;
ca2beaf8 2077 key_info = spk_our_keys[keycode];
ff471ea8 2078 if (!key_info)
c6e3fd22
WH
2079 goto no_map;
2080 /* Check valid read all mode keys */
2081 if ((cursor_track == read_all_mode) && (!up_flag)) {
2082 switch (value) {
2083 case KVAL(K_DOWN):
2084 case KVAL(K_UP):
2085 case KVAL(K_LEFT):
2086 case KVAL(K_RIGHT):
2087 case KVAL(K_PGUP):
2088 case KVAL(K_PGDN):
2089 break;
2090 default:
2091 stop_read_all(vc);
2092 break;
2093 }
2094 }
16d35515 2095 shift_info = (shift_state & 0x0f) + key_speakup;
ca2beaf8 2096 offset = spk_shift_table[shift_info];
c6e3fd22
WH
2097 if (offset) {
2098 new_key = key_info[offset];
2099 if (new_key) {
2100 ret = 1;
2101 if (new_key == SPK_KEY) {
2102 if (!spk_key_locked)
2103 key_speakup = (up_flag) ? 0 : 16;
2104 if (up_flag || spk_killed)
2105 goto out;
2106 spk_shut_up &= 0xfe;
ca2beaf8 2107 spk_do_flush();
c6e3fd22
WH
2108 goto out;
2109 }
2110 if (up_flag)
2111 goto out;
2112 if (last_keycode == keycode &&
3d3cb1bf 2113 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
c6e3fd22 2114 spk_close_press = 1;
ca2beaf8 2115 offset = spk_shift_table[shift_info + 32];
16d35515 2116 /* double press? */
c6e3fd22
WH
2117 if (offset && key_info[offset])
2118 new_key = key_info[offset];
2119 }
2120 last_keycode = keycode;
2121 last_spk_jiffy = jiffies;
2122 type = KT_SPKUP;
2123 value = new_key;
2124 }
2125 }
2126no_map:
ca2beaf8 2127 if (type == KT_SPKUP && spk_special_handler == NULL) {
c6e3fd22
WH
2128 do_spkup(vc, new_key);
2129 spk_close_press = 0;
2130 ret = 1;
2131 goto out;
2132 }
2133 if (up_flag || spk_killed || type == KT_SHIFT)
2134 goto out;
2135 spk_shut_up &= 0xfe;
2136 kh = (value == KVAL(K_DOWN))
16d35515
WH
2137 || (value == KVAL(K_UP))
2138 || (value == KVAL(K_LEFT))
2139 || (value == KVAL(K_RIGHT));
c6e3fd22 2140 if ((cursor_track != read_all_mode) || !kh)
ca2beaf8
ST
2141 if (!spk_no_intr)
2142 spk_do_flush();
2143 if (spk_special_handler) {
c6e3fd22
WH
2144 if (type == KT_SPEC && value == 1) {
2145 value = '\n';
2146 type = KT_LATIN;
2147 } else if (type == KT_LETTER)
2148 type = KT_LATIN;
2149 else if (value == 0x7f)
16d35515 2150 value = 8; /* make del = backspace */
ca2beaf8 2151 ret = (*spk_special_handler) (vc, type, value, keycode);
c6e3fd22
WH
2152 spk_close_press = 0;
2153 if (ret < 0)
2154 bleep(9);
2155 goto out;
2156 }
2157 last_keycode = 0;
2158out:
3efe810f 2159 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
2160 return ret;
2161}
2162
2163static int keyboard_notifier_call(struct notifier_block *nb,
16d35515 2164 unsigned long code, void *_param)
c6e3fd22
WH
2165{
2166 struct keyboard_notifier_param *param = _param;
2167 struct vc_data *vc = param->vc;
2168 int up = !param->down;
2169 int ret = NOTIFY_OK;
16d35515 2170 static int keycode; /* to hold the current keycode */
c6e3fd22
WH
2171
2172 if (vc->vc_mode == KD_GRAPHICS)
2173 return ret;
2174
2175 /*
2176 * First, determine whether we are handling a fake keypress on
2177 * the current processor. If we are, then return NOTIFY_OK,
2178 * to pass the keystroke up the chain. This prevents us from
2179 * trying to take the Speakup lock while it is held by the
2180 * processor on which the simulated keystroke was generated.
2181 * Also, the simulated keystrokes should be ignored by Speakup.
2182 */
2183
2184 if (speakup_fake_key_pressed())
2185 return ret;
2186
2187 switch (code) {
2188 case KBD_KEYCODE:
2189 /* speakup requires keycode and keysym currently */
2190 keycode = param->value;
2191 break;
2192 case KBD_UNBOUND_KEYCODE:
2193 /* not used yet */
2194 break;
2195 case KBD_UNICODE:
2196 /* not used yet */
2197 break;
2198 case KBD_KEYSYM:
2199 if (speakup_key(vc, param->shift, keycode, param->value, up))
2200 ret = NOTIFY_STOP;
16d35515
WH
2201 else if (KTYP(param->value) == KT_CUR)
2202 ret = pre_handle_cursor(vc, KVAL(param->value), up);
c6e3fd22 2203 break;
16d35515
WH
2204 case KBD_POST_KEYSYM:{
2205 unsigned char type = KTYP(param->value) - 0xf0;
2206 unsigned char val = KVAL(param->value);
8e69a811 2207
16d35515
WH
2208 switch (type) {
2209 case KT_SHIFT:
2210 do_handle_shift(vc, val, up);
2211 break;
2212 case KT_LATIN:
2213 case KT_LETTER:
2214 do_handle_latin(vc, val, up);
2215 break;
2216 case KT_CUR:
2217 do_handle_cursor(vc, val, up);
2218 break;
2219 case KT_SPEC:
2220 do_handle_spec(vc, val, up);
2221 break;
2222 }
c6e3fd22
WH
2223 break;
2224 }
c6e3fd22
WH
2225 }
2226 return ret;
2227}
2228
2229static int vt_notifier_call(struct notifier_block *nb,
16d35515 2230 unsigned long code, void *_param)
c6e3fd22
WH
2231{
2232 struct vt_notifier_param *param = _param;
2233 struct vc_data *vc = param->vc;
8e69a811 2234
c6e3fd22
WH
2235 switch (code) {
2236 case VT_ALLOCATE:
2237 if (vc->vc_mode == KD_TEXT)
2238 speakup_allocate(vc);
2239 break;
2240 case VT_DEALLOCATE:
2241 speakup_deallocate(vc);
2242 break;
2243 case VT_WRITE:
2244 if (param->c == '\b')
2245 speakup_bs(vc);
16d35515
WH
2246 else if (param->c < 0x100) {
2247 char d = param->c;
0a3a725a 2248
16d35515
WH
2249 speakup_con_write(vc, &d, 1);
2250 }
c6e3fd22
WH
2251 break;
2252 case VT_UPDATE:
2253 speakup_con_update(vc);
2254 break;
2255 }
2256 return NOTIFY_OK;
2257}
2258
2259/* called by: module_exit() */
2260static void __exit speakup_exit(void)
2261{
2262 int i;
2263
c6e3fd22
WH
2264 unregister_keyboard_notifier(&keyboard_notifier_block);
2265 unregister_vt_notifier(&vt_notifier_block);
2266 speakup_unregister_devsynth();
d7500135 2267 speakup_cancel_paste();
c6e3fd22 2268 del_timer(&cursor_timer);
c6e3fd22
WH
2269 kthread_stop(speakup_task);
2270 speakup_task = NULL;
2271 mutex_lock(&spk_mutex);
2272 synth_release();
2273 mutex_unlock(&spk_mutex);
2274
628f3428
CB
2275 speakup_kobj_exit();
2276
2277 for (i = 0; i < MAX_NR_CONSOLES; i++)
2278 kfree(speakup_console[i]);
2279
2280 speakup_remove_virtual_keyboard();
2281
c6e3fd22
WH
2282 for (i = 0; i < MAXVARS; i++)
2283 speakup_unregister_var(i);
2284
2285 for (i = 0; i < 256; i++) {
ca2beaf8
ST
2286 if (spk_characters[i] != spk_default_chars[i])
2287 kfree(spk_characters[i]);
c6e3fd22 2288 }
628f3428 2289
ca2beaf8 2290 spk_free_user_msgs();
c6e3fd22
WH
2291}
2292
2293/* call by: module_init() */
2294static int __init speakup_init(void)
2295{
2296 int i;
628f3428 2297 long err = 0;
c6e3fd22
WH
2298 struct st_spk_t *first_console;
2299 struct vc_data *vc = vc_cons[fg_console].d;
2300 struct var_t *var;
2301
628f3428 2302 /* These first few initializations cannot fail. */
ca2beaf8
ST
2303 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2304 spk_reset_default_chars();
2305 spk_reset_default_chartab();
2306 spk_strlwr(synth_name);
c6e3fd22 2307 spk_vars[0].u.n.high = vc->vc_cols;
16d35515 2308 for (var = spk_vars; var->var_id != MAXVARS; var++)
c6e3fd22 2309 speakup_register_var(var);
16d35515
WH
2310 for (var = synth_time_vars;
2311 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
c6e3fd22 2312 speakup_register_var(var);
ca2beaf8 2313 for (i = 1; spk_punc_info[i].mask != 0; i++)
ff471ea8 2314 spk_set_mask_bits(NULL, i, 2);
c6e3fd22 2315
ca2beaf8 2316 spk_set_key_info(spk_key_defaults, spk_key_buf);
c6e3fd22 2317
628f3428
CB
2318 /* From here on out, initializations can fail. */
2319 err = speakup_add_virtual_keyboard();
2320 if (err)
2321 goto error_virtkeyboard;
2322
2323 first_console = kzalloc(sizeof(*first_console), GFP_KERNEL);
2324 if (!first_console) {
2325 err = -ENOMEM;
2326 goto error_alloc;
2327 }
2328
2329 speakup_console[vc->vc_num] = first_console;
2330 speakup_date(vc);
2331
c6e3fd22 2332 for (i = 0; i < MAX_NR_CONSOLES; i++)
628f3428
CB
2333 if (vc_cons[i].d) {
2334 err = speakup_allocate(vc_cons[i].d);
2335 if (err)
2336 goto error_kobjects;
2337 }
2338
ca2beaf8 2339 if (spk_quiet_boot)
4afaee15
CB
2340 spk_shut_up |= 0x01;
2341
628f3428
CB
2342 err = speakup_kobj_init();
2343 if (err)
2344 goto error_kobjects;
c6e3fd22 2345
c6e3fd22
WH
2346 synth_init(synth_name);
2347 speakup_register_devsynth();
628f3428
CB
2348 /*
2349 * register_devsynth might fail, but this error is not fatal.
2350 * /dev/synth is an extra feature; the rest of Speakup
2351 * will work fine without it.
2352 */
c6e3fd22 2353
628f3428
CB
2354 err = register_keyboard_notifier(&keyboard_notifier_block);
2355 if (err)
2356 goto error_kbdnotifier;
2357 err = register_vt_notifier(&vt_notifier_block);
2358 if (err)
2359 goto error_vtnotifier;
c6e3fd22
WH
2360
2361 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
628f3428 2362
7959d556 2363 if (IS_ERR(speakup_task)) {
628f3428
CB
2364 err = PTR_ERR(speakup_task);
2365 goto error_task;
7959d556 2366 }
628f3428
CB
2367
2368 set_user_nice(speakup_task, 10);
7959d556 2369 wake_up_process(speakup_task);
628f3428
CB
2370
2371 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2372 pr_info("synth name on entry is: %s\n", synth_name);
7959d556
WH
2373 goto out;
2374
628f3428
CB
2375error_task:
2376 unregister_vt_notifier(&vt_notifier_block);
2377
2378error_vtnotifier:
2379 unregister_keyboard_notifier(&keyboard_notifier_block);
2380 del_timer(&cursor_timer);
2381
2382error_kbdnotifier:
2383 speakup_unregister_devsynth();
2384 mutex_lock(&spk_mutex);
2385 synth_release();
2386 mutex_unlock(&spk_mutex);
2387 speakup_kobj_exit();
2388
2389error_kobjects:
2390 for (i = 0; i < MAX_NR_CONSOLES; i++)
2391 kfree(speakup_console[i]);
2392
2393error_alloc:
7959d556 2394 speakup_remove_virtual_keyboard();
628f3428
CB
2395
2396error_virtkeyboard:
2397 for (i = 0; i < MAXVARS; i++)
2398 speakup_unregister_var(i);
2399
2400 for (i = 0; i < 256; i++) {
ca2beaf8
ST
2401 if (spk_characters[i] != spk_default_chars[i])
2402 kfree(spk_characters[i]);
628f3428
CB
2403 }
2404
ca2beaf8 2405 spk_free_user_msgs();
628f3428 2406
7959d556
WH
2407out:
2408 return err;
c6e3fd22
WH
2409}
2410
c6e3fd22
WH
2411module_init(speakup_init);
2412module_exit(speakup_exit);