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