]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/staging/speakup/main.c
staging: serqt_usb2: Remove NULL check before kfree
[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.
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 */
628f3428 1286int speakup_allocate(struct vc_data *vc)
c6e3fd22
WH
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 1294 if (speakup_console[vc_num] == NULL)
628f3428 1295 return -ENOMEM;
c6e3fd22
WH
1296 speakup_date(vc);
1297 } else if (!spk_parked)
1298 speakup_date(vc);
628f3428
CB
1299
1300 return 0;
c6e3fd22
WH
1301}
1302
1303void speakup_deallocate(struct vc_data *vc)
1304{
1305 int vc_num;
1306
1307 vc_num = vc->vc_num;
1308 if (speakup_console[vc_num] != NULL) {
1309 kfree(speakup_console[vc_num]);
1310 speakup_console[vc_num] = NULL;
1311 }
1312}
1313
1314static u_char is_cursor;
1315static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1316static int cursor_con;
1317
1318static void reset_highlight_buffers(struct vc_data *);
1319
1320static int read_all_key;
1321
c6e3fd22
WH
1322static void start_read_all_timer(struct vc_data *vc, int command);
1323
1324enum {
1325 RA_NOTHING,
1326 RA_NEXT_SENT,
1327 RA_PREV_LINE,
1328 RA_NEXT_LINE,
1329 RA_PREV_SENT,
1330 RA_DOWN_ARROW,
1331 RA_TIMER,
1332 RA_FIND_NEXT_SENT,
1333 RA_FIND_PREV_SENT,
1334};
1335
16d35515 1336static void kbd_fakekey2(struct vc_data *vc, int command)
c6e3fd22
WH
1337{
1338 del_timer(&cursor_timer);
1339 speakup_fake_down_arrow();
1340 start_read_all_timer(vc, command);
1341}
1342
16d35515 1343static void read_all_doc(struct vc_data *vc)
c6e3fd22
WH
1344{
1345 if ((vc->vc_num != fg_console) || synth == NULL || spk_shut_up)
1346 return;
1347 if (!synth_supports_indexing())
1348 return;
1349 if (cursor_track != read_all_mode)
1350 prev_cursor_track = cursor_track;
1351 cursor_track = read_all_mode;
1352 reset_index_count(0);
1353 if (get_sentence_buf(vc, 0) == -1)
1354 kbd_fakekey2(vc, RA_DOWN_ARROW);
1355 else {
1356 say_sentence_num(0, 0);
1357 synth_insert_next_index(0);
1358 start_read_all_timer(vc, RA_TIMER);
1359 }
1360}
1361
16d35515 1362static void stop_read_all(struct vc_data *vc)
c6e3fd22
WH
1363{
1364 del_timer(&cursor_timer);
1365 cursor_track = prev_cursor_track;
1366 spk_shut_up &= 0xfe;
1367 do_flush();
1368}
1369
16d35515 1370static void start_read_all_timer(struct vc_data *vc, int command)
c6e3fd22
WH
1371{
1372 struct var_t *cursor_timeout;
1373
1374 cursor_con = vc->vc_num;
1375 read_all_key = command;
1376 cursor_timeout = get_var(CURSOR_TIME);
16d35515
WH
1377 mod_timer(&cursor_timer,
1378 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
c6e3fd22
WH
1379}
1380
16d35515 1381static void handle_cursor_read_all(struct vc_data *vc, int command)
c6e3fd22
WH
1382{
1383 int indcount, sentcount, rv, sn;
1384
1385 switch (command) {
1386 case RA_NEXT_SENT:
1387 /* Get Current Sentence */
1388 get_index_count(&indcount, &sentcount);
1389 /*printk("%d %d ", indcount, sentcount); */
16d35515 1390 reset_index_count(sentcount + 1);
c6e3fd22 1391 if (indcount == 1) {
16d35515 1392 if (!say_sentence_num(sentcount + 1, 0)) {
c6e3fd22
WH
1393 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1394 return;
1395 }
1396 synth_insert_next_index(0);
1397 } else {
1398 sn = 0;
16d35515 1399 if (!say_sentence_num(sentcount + 1, 1)) {
c6e3fd22
WH
1400 sn = 1;
1401 reset_index_count(sn);
1402 } else
1403 synth_insert_next_index(0);
1404 if (!say_sentence_num(sn, 0)) {
1405 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1406 return;
1407 }
1408 synth_insert_next_index(0);
1409 }
1410 start_read_all_timer(vc, RA_TIMER);
1411 break;
1412 case RA_PREV_SENT:
1413 break;
1414 case RA_NEXT_LINE:
1415 read_all_doc(vc);
1416 break;
1417 case RA_PREV_LINE:
1418 break;
1419 case RA_DOWN_ARROW:
1420 if (get_sentence_buf(vc, 0) == -1) {
1421 kbd_fakekey2(vc, RA_DOWN_ARROW);
1422 } else {
1423 say_sentence_num(0, 0);
1424 synth_insert_next_index(0);
1425 start_read_all_timer(vc, RA_TIMER);
1426 }
1427 break;
1428 case RA_FIND_NEXT_SENT:
1429 rv = get_sentence_buf(vc, 0);
1430 if (rv == -1)
1431 read_all_doc(vc);
1432 if (rv == 0)
1433 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1434 else {
1435 say_sentence_num(1, 0);
1436 synth_insert_next_index(0);
1437 start_read_all_timer(vc, RA_TIMER);
1438 }
1439 break;
1440 case RA_FIND_PREV_SENT:
1441 break;
1442 case RA_TIMER:
1443 get_index_count(&indcount, &sentcount);
1444 if (indcount < 2)
1445 kbd_fakekey2(vc, RA_DOWN_ARROW);
1446 else
1447 start_read_all_timer(vc, RA_TIMER);
1448 break;
1449 }
1450}
1451
1452static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1453{
1454 unsigned long flags;
1455 spk_lock(flags);
1456 if (cursor_track == read_all_mode) {
1457 spk_parked &= 0xfe;
1458 if (synth == NULL || up_flag || spk_shut_up) {
1459 spk_unlock(flags);
1460 return NOTIFY_STOP;
1461 }
1462 del_timer(&cursor_timer);
1463 spk_shut_up &= 0xfe;
1464 do_flush();
16d35515 1465 start_read_all_timer(vc, value + 1);
c6e3fd22
WH
1466 spk_unlock(flags);
1467 return NOTIFY_STOP;
1468 }
1469 spk_unlock(flags);
1470 return NOTIFY_OK;
1471}
1472
1473static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1474{
1475 unsigned long flags;
1476 struct var_t *cursor_timeout;
1477
1478 spk_lock(flags);
1479 spk_parked &= 0xfe;
1480 if (synth == NULL || up_flag || spk_shut_up || cursor_track == CT_Off) {
1481 spk_unlock(flags);
1482 return;
1483 }
1484 spk_shut_up &= 0xfe;
1485 if (no_intr)
1486 do_flush();
1487/* the key press flushes if !no_inter but we want to flush on cursor
1488 * moves regardless of no_inter state */
1489 is_cursor = value + 1;
1490 old_cursor_pos = vc->vc_pos;
1491 old_cursor_x = vc->vc_x;
1492 old_cursor_y = vc->vc_y;
1493 speakup_console[vc->vc_num]->ht.cy = vc->vc_y;
1494 cursor_con = vc->vc_num;
1495 if (cursor_track == CT_Highlight)
1496 reset_highlight_buffers(vc);
1497 cursor_timeout = get_var(CURSOR_TIME);
16d35515
WH
1498 mod_timer(&cursor_timer,
1499 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
c6e3fd22
WH
1500 spk_unlock(flags);
1501}
1502
16d35515 1503static void update_color_buffer(struct vc_data *vc, const char *ic, int len)
c6e3fd22
WH
1504{
1505 int i, bi, hi;
1506 int vc_num = vc->vc_num;
1507
16d35515 1508 bi = ((vc->vc_attr & 0x70) >> 4);
c6e3fd22
WH
1509 hi = speakup_console[vc_num]->ht.highsize[bi];
1510
1511 i = 0;
1512 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1513 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1514 speakup_console[vc_num]->ht.rx[bi] = vc->vc_x;
1515 speakup_console[vc_num]->ht.ry[bi] = vc->vc_y;
1516 }
1517 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1518 if ((ic[i] > 32) && (ic[i] < 127)) {
1519 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1520 hi++;
1521 } else if ((ic[i] == 32) && (hi != 0)) {
16d35515
WH
1522 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1523 32) {
c6e3fd22 1524 speakup_console[vc_num]->ht.highbuf[bi][hi] =
16d35515 1525 ic[i];
c6e3fd22
WH
1526 hi++;
1527 }
1528 }
1529 i++;
1530 }
1531 speakup_console[vc_num]->ht.highsize[bi] = hi;
1532}
1533
16d35515 1534static void reset_highlight_buffers(struct vc_data *vc)
c6e3fd22
WH
1535{
1536 int i;
1537 int vc_num = vc->vc_num;
16d35515 1538 for (i = 0; i < 8; i++)
c6e3fd22
WH
1539 speakup_console[vc_num]->ht.highsize[i] = 0;
1540}
1541
16d35515 1542static int count_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1543{
1544 int i, bg;
1545 int cc;
1546 int vc_num = vc->vc_num;
1547 u16 ch;
1548 u16 *start = (u16 *) vc->vc_origin;
1549
1550 for (i = 0; i < 8; i++)
1551 speakup_console[vc_num]->ht.bgcount[i] = 0;
1552
1553 for (i = 0; i < vc->vc_rows; i++) {
16d35515 1554 u16 *end = start + vc->vc_cols * 2;
c6e3fd22
WH
1555 u16 *ptr;
1556 for (ptr = start; ptr < end; ptr++) {
1557 ch = get_attributes(ptr);
1558 bg = (ch & 0x70) >> 4;
1559 speakup_console[vc_num]->ht.bgcount[bg]++;
1560 }
1561 start += vc->vc_size_row;
1562 }
1563
1564 cc = 0;
1565 for (i = 0; i < 8; i++)
1566 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1567 cc++;
1568 return cc;
1569}
1570
16d35515 1571static int get_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1572{
1573 int i, j;
1574 unsigned int cptr[8], tmp;
1575 int vc_num = vc->vc_num;
1576
1577 for (i = 0; i < 8; i++)
1578 cptr[i] = i;
1579
1580 for (i = 0; i < 7; i++)
1581 for (j = i + 1; j < 8; j++)
1582 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
16d35515 1583 speakup_console[vc_num]->ht.bgcount[cptr[j]]) {
c6e3fd22
WH
1584 tmp = cptr[i];
1585 cptr[i] = cptr[j];
1586 cptr[j] = tmp;
1587 }
1588
1589 for (i = 0; i < 8; i++)
1590 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1591 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1592 return cptr[i];
1593 return -1;
1594}
1595
16d35515 1596static int speak_highlight(struct vc_data *vc)
c6e3fd22
WH
1597{
1598 int hc, d;
1599 int vc_num = vc->vc_num;
1600 if (count_highlight_color(vc) == 1)
1601 return 0;
1602 hc = get_highlight_color(vc);
1603 if (hc != -1) {
16d35515 1604 d = vc->vc_y - speakup_console[vc_num]->ht.cy;
c6e3fd22
WH
1605 if ((d == 1) || (d == -1))
1606 if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y)
1607 return 0;
1608 spk_parked |= 0x01;
1609 do_flush();
1610 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
16d35515 1611 speakup_console[vc_num]->ht.highsize[hc]);
c6e3fd22
WH
1612 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1613 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1614 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1615 return 1;
1616 }
1617 return 0;
1618}
1619
16d35515 1620static void cursor_done(u_long data)
c6e3fd22
WH
1621{
1622 struct vc_data *vc = vc_cons[cursor_con].d;
1623 unsigned long flags;
1624 del_timer(&cursor_timer);
1625 spk_lock(flags);
1626 if (cursor_con != fg_console) {
1627 is_cursor = 0;
1628 goto out;
1629 }
1630 speakup_date(vc);
1631 if (win_enabled) {
1632 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1633 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
c6e3fd22
WH
1634 spk_keydown = is_cursor = 0;
1635 goto out;
1636 }
1637 }
1638 if (cursor_track == read_all_mode) {
1639 handle_cursor_read_all(vc, read_all_key);
1640 goto out;
1641 }
1642 if (cursor_track == CT_Highlight) {
1643 if (speak_highlight(vc)) {
1644 spk_keydown = is_cursor = 0;
1645 goto out;
1646 }
1647 }
1648 if (cursor_track == CT_Window)
1649 speakup_win_say(vc);
1650 else if (is_cursor == 1 || is_cursor == 4)
1651 say_line_from_to(vc, 0, vc->vc_cols, 0);
1652 else
1653 say_char(vc);
1654 spk_keydown = is_cursor = 0;
1655out:
1656 spk_unlock(flags);
1657}
1658
1659/* called by: vt_notifier_call() */
1660static void speakup_bs(struct vc_data *vc)
1661{
1662 unsigned long flags;
1663 if (!speakup_console[vc->vc_num])
1664 return;
1665 if (!spk_trylock(flags))
1666 /* Speakup output, discard */
1667 return;
1668 if (!spk_parked)
1669 speakup_date(vc);
1670 if (spk_shut_up || synth == NULL) {
1671 spk_unlock(flags);
1672 return;
1673 }
1674 if (vc->vc_num == fg_console && spk_keydown) {
1675 spk_keydown = 0;
1676 if (!is_cursor)
1677 say_char(vc);
1678 }
1679 spk_unlock(flags);
1680}
1681
1682/* called by: vt_notifier_call() */
1683static void speakup_con_write(struct vc_data *vc, const char *str, int len)
1684{
1685 unsigned long flags;
1686 if ((vc->vc_num != fg_console) || spk_shut_up || synth == NULL)
1687 return;
1688 if (!spk_trylock(flags))
1689 /* Speakup output, discard */
1690 return;
1691 if (bell_pos && spk_keydown && (vc->vc_x == bell_pos - 1))
1692 bleep(3);
1693 if ((is_cursor) || (cursor_track == read_all_mode)) {
1694 if (cursor_track == CT_Highlight)
1695 update_color_buffer(vc, str, len);
1696 spk_unlock(flags);
1697 return;
1698 }
1699 if (win_enabled) {
1700 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1701 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
c6e3fd22
WH
1702 spk_unlock(flags);
1703 return;
1704 }
1705 }
1706
1707 spkup_write(str, len);
1708 spk_unlock(flags);
1709}
1710
16d35515 1711void speakup_con_update(struct vc_data *vc)
c6e3fd22
WH
1712{
1713 unsigned long flags;
1714 if (speakup_console[vc->vc_num] == NULL || spk_parked)
1715 return;
1716 if (!spk_trylock(flags))
1717 /* Speakup output, discard */
1718 return;
1719 speakup_date(vc);
1720 spk_unlock(flags);
1721}
1722
1723static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1724{
1725 unsigned long flags;
1726 int on_off = 2;
1727 char *label;
1728 if (synth == NULL || up_flag || spk_killed)
1729 return;
1730 spk_lock(flags);
1731 spk_shut_up &= 0xfe;
1732 if (no_intr)
1733 do_flush();
1734 switch (value) {
1735 case KVAL(K_CAPS):
1736 label = msg_get(MSG_KEYNAME_CAPSLOCK);
1737 on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_CAPSLOCK));
1738 break;
1739 case KVAL(K_NUM):
1740 label = msg_get(MSG_KEYNAME_NUMLOCK);
1741 on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_NUMLOCK));
1742 break;
1743 case KVAL(K_HOLD):
1744 label = msg_get(MSG_KEYNAME_SCROLLLOCK);
1745 on_off = (vc_kbd_led(kbd_table + vc->vc_num, VC_SCROLLOCK));
1746 if (speakup_console[vc->vc_num])
1747 speakup_console[vc->vc_num]->tty_stopped = on_off;
1748 break;
1749 default:
1750 spk_parked &= 0xfe;
1751 spk_unlock(flags);
1752 return;
1753 }
1754 if (on_off < 2)
1755 synth_printf("%s %s\n",
1756 label, msg_get(MSG_STATUS_START + on_off));
1757 spk_unlock(flags);
1758}
1759
16d35515 1760static int inc_dec_var(u_char value)
c6e3fd22
WH
1761{
1762 struct st_var_header *p_header;
1763 struct var_t *var_data;
1764 char num_buf[32];
1765 char *cp = num_buf;
1766 char *pn;
1767 int var_id = (int)value - VAR_START;
16d35515
WH
1768 int how = (var_id & 1) ? E_INC : E_DEC;
1769 var_id = var_id / 2 + FIRST_SET_VAR;
c6e3fd22
WH
1770 p_header = get_var_header(var_id);
1771 if (p_header == NULL)
1772 return -1;
1773 if (p_header->var_type != VAR_NUM)
1774 return -1;
1775 var_data = p_header->data;
1776 if (set_num_var(1, p_header, how) != 0)
1777 return -1;
1778 if (!spk_close_press) {
1779 for (pn = p_header->name; *pn; pn++) {
1780 if (*pn == '_')
1781 *cp = SPACE;
1782 else
1783 *cp++ = *pn;
1784 }
1785 }
1786 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
16d35515 1787 var_data->u.n.value);
c6e3fd22
WH
1788 synth_printf("%s", num_buf);
1789 return 0;
1790}
1791
16d35515 1792static void speakup_win_set(struct vc_data *vc)
c6e3fd22
WH
1793{
1794 char info[40];
1795 if (win_start > 1) {
1796 synth_printf("%s\n", msg_get(MSG_WINDOW_ALREADY_SET));
1797 return;
1798 }
1799 if (spk_x < win_left || spk_y < win_top) {
1800 synth_printf("%s\n", msg_get(MSG_END_BEFORE_START));
1801 return;
1802 }
1803 if (win_start && spk_x == win_left && spk_y == win_top) {
1804 win_left = 0;
16d35515 1805 win_right = vc->vc_cols - 1;
c6e3fd22
WH
1806 win_bottom = spk_y;
1807 snprintf(info, sizeof(info), msg_get(MSG_WINDOW_LINE),
16d35515 1808 (int)win_top + 1);
c6e3fd22
WH
1809 } else {
1810 if (!win_start) {
1811 win_top = spk_y;
1812 win_left = spk_x;
1813 } else {
1814 win_bottom = spk_y;
1815 win_right = spk_x;
1816 }
1817 snprintf(info, sizeof(info), msg_get(MSG_WINDOW_BOUNDARY),
16d35515
WH
1818 (win_start) ? msg_get(MSG_END) : msg_get(MSG_START),
1819 (int)spk_y + 1, (int)spk_x + 1);
c6e3fd22
WH
1820 }
1821 synth_printf("%s\n", info);
1822 win_start++;
1823}
1824
16d35515 1825static void speakup_win_clear(struct vc_data *vc)
c6e3fd22
WH
1826{
1827 win_top = win_bottom = 0;
1828 win_left = win_right = 0;
1829 win_start = 0;
1830 synth_printf("%s\n", msg_get(MSG_WINDOW_CLEARED));
1831}
1832
16d35515 1833static void speakup_win_enable(struct vc_data *vc)
c6e3fd22
WH
1834{
1835 if (win_start < 2) {
1836 synth_printf("%s\n", msg_get(MSG_NO_WINDOW));
1837 return;
1838 }
1839 win_enabled ^= 1;
1840 if (win_enabled)
1841 synth_printf("%s\n", msg_get(MSG_WINDOW_SILENCED));
1842 else
1843 synth_printf("%s\n", msg_get(MSG_WINDOW_SILENCE_DISABLED));
1844}
1845
16d35515 1846static void speakup_bits(struct vc_data *vc)
c6e3fd22
WH
1847{
1848 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
1849 if (special_handler != NULL || val < 1 || val > 6) {
1850 synth_printf("%s\n", msg_get(MSG_ERROR));
1851 return;
1852 }
1853 pb_edit = &punc_info[val];
1854 synth_printf(msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1855 special_handler = edit_bits;
1856}
1857
1858static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1859{
1860 static u_char *goto_buf = "\0\0\0\0\0\0";
16d35515 1861 static int num;
c6e3fd22
WH
1862 int maxlen, go_pos;
1863 char *cp;
1864 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1865 goto do_goto;
1866 if (type == KT_LATIN && ch == '\n')
1867 goto do_goto;
1868 if (type != 0)
1869 goto oops;
1870 if (ch == 8) {
1871 if (num == 0)
1872 return -1;
1873 ch = goto_buf[--num];
1874 goto_buf[num] = '\0';
1875 spkup_write(&ch, 1);
1876 return 1;
1877 }
1878 if (ch < '+' || ch > 'y')
1879 goto oops;
1880 goto_buf[num++] = ch;
1881 goto_buf[num] = '\0';
1882 spkup_write(&ch, 1);
1883 maxlen = (*goto_buf >= '0') ? 3 : 4;
1884 if ((ch == '+' || ch == '-') && num == 1)
1885 return 1;
1886 if (ch >= '0' && ch <= '9' && num < maxlen)
1887 return 1;
16d35515 1888 if (num < maxlen - 1 || num > maxlen)
c6e3fd22
WH
1889 goto oops;
1890 if (ch < 'x' || ch > 'y') {
1891oops:
1892 if (!spk_killed)
1893 synth_printf(" %s\n", msg_get(MSG_GOTO_CANCELED));
1894 goto_buf[num = 0] = '\0';
1895 special_handler = NULL;
1896 return 1;
1897 }
1898 cp = speakup_s2i(goto_buf, &go_pos);
16d35515 1899 goto_pos = (u_long) go_pos;
c6e3fd22
WH
1900 if (*cp == 'x') {
1901 if (*goto_buf < '0')
1902 goto_pos += spk_x;
1903 else
1904 goto_pos--;
1905 if (goto_pos < 0)
1906 goto_pos = 0;
1907 if (goto_pos >= vc->vc_cols)
16d35515 1908 goto_pos = vc->vc_cols - 1;
c6e3fd22
WH
1909 goto_x = 1;
1910 } else {
1911 if (*goto_buf < '0')
1912 goto_pos += spk_y;
1913 else
1914 goto_pos--;
1915 if (goto_pos < 0)
1916 goto_pos = 0;
1917 if (goto_pos >= vc->vc_rows)
16d35515 1918 goto_pos = vc->vc_rows - 1;
c6e3fd22
WH
1919 goto_x = 0;
1920 }
16d35515 1921 goto_buf[num = 0] = '\0';
c6e3fd22
WH
1922do_goto:
1923 special_handler = NULL;
1924 spk_parked |= 0x01;
1925 if (goto_x) {
1926 spk_pos -= spk_x * 2;
1927 spk_x = goto_pos;
1928 spk_pos += goto_pos * 2;
1929 say_word(vc);
1930 } else {
1931 spk_y = goto_pos;
1932 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
1933 say_line(vc);
1934 }
1935 return 1;
1936}
1937
16d35515 1938static void speakup_goto(struct vc_data *vc)
c6e3fd22
WH
1939{
1940 if (special_handler != NULL) {
1941 synth_printf("%s\n", msg_get(MSG_ERROR));
1942 return;
1943 }
1944 synth_printf("%s\n", msg_get(MSG_GOTO));
1945 special_handler = handle_goto;
1946 return;
1947}
1948
1949static void speakup_help(struct vc_data *vc)
1950{
1951 handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
1952}
1953
16d35515 1954static void do_nothing(struct vc_data *vc)
c6e3fd22 1955{
16d35515 1956 return; /* flush done in do_spkup */
c6e3fd22 1957}
16d35515 1958
c6e3fd22
WH
1959static u_char key_speakup, spk_key_locked;
1960
16d35515 1961static void speakup_lock(struct vc_data *vc)
c6e3fd22
WH
1962{
1963 if (!spk_key_locked)
1964 spk_key_locked = key_speakup = 16;
1965 else
1966 spk_key_locked = key_speakup = 0;
1967}
1968
16d35515 1969typedef void (*spkup_hand) (struct vc_data *);
c6e3fd22
WH
1970spkup_hand spkup_handler[] = {
1971 /* must be ordered same as defines in speakup.h */
1972 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
1973 speakup_cut, speakup_paste, say_first_char, say_last_char,
1974 say_char, say_prev_char, say_next_char,
1975 say_word, say_prev_word, say_next_word,
1976 say_line, say_prev_line, say_next_line,
1977 top_edge, bottom_edge, left_edge, right_edge,
1978 spell_word, spell_word, say_screen,
1979 say_position, say_attributes,
16d35515 1980 speakup_off, speakup_parked, say_line, /* this is for indent */
c6e3fd22
WH
1981 say_from_top, say_to_bottom,
1982 say_from_left, say_to_right,
1983 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
1984 speakup_bits, speakup_bits, speakup_bits,
1985 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
1986 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
1987};
1988
1989static void do_spkup(struct vc_data *vc, u_char value)
1990{
1991 if (spk_killed && value != SPEECH_KILL)
1992 return;
1993 spk_keydown = 0;
1994 spk_lastkey = 0;
1995 spk_shut_up &= 0xfe;
1996 this_speakup_key = value;
1997 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
1998 do_flush();
16d35515 1999 (*spkup_handler[value]) (vc);
c6e3fd22
WH
2000 } else {
2001 if (inc_dec_var(value) < 0)
2002 bleep(9);
2003 }
2004}
2005
2006static const char *pad_chars = "0123456789+-*/\015,.?()";
2007
2008int
2009speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
16d35515 2010 int up_flag)
c6e3fd22
WH
2011{
2012 unsigned long flags;
2013 int kh;
2014 u_char *key_info;
2015 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2016 u_char shift_info, offset;
2017 int ret = 0;
2018 if (synth == NULL)
2019 return 0;
2020
2021 spk_lock(flags);
5b19208a 2022 tty = vc->port.tty;
c6e3fd22
WH
2023 if (type >= 0xf0)
2024 type -= 0xf0;
16d35515
WH
2025 if (type == KT_PAD
2026 && (vc_kbd_led(kbd_table + fg_console, VC_NUMLOCK))) {
c6e3fd22
WH
2027 if (up_flag) {
2028 spk_keydown = 0;
2029 goto out;
2030 }
2031 value = spk_lastkey = pad_chars[value];
2032 spk_keydown++;
2033 spk_parked &= 0xfe;
2034 goto no_map;
2035 }
2036 if (keycode >= MAX_KEY)
2037 goto no_map;
2038 key_info = our_keys[keycode];
2039 if (key_info == 0)
2040 goto no_map;
2041 /* Check valid read all mode keys */
2042 if ((cursor_track == read_all_mode) && (!up_flag)) {
2043 switch (value) {
2044 case KVAL(K_DOWN):
2045 case KVAL(K_UP):
2046 case KVAL(K_LEFT):
2047 case KVAL(K_RIGHT):
2048 case KVAL(K_PGUP):
2049 case KVAL(K_PGDN):
2050 break;
2051 default:
2052 stop_read_all(vc);
2053 break;
2054 }
2055 }
16d35515 2056 shift_info = (shift_state & 0x0f) + key_speakup;
c6e3fd22
WH
2057 offset = shift_table[shift_info];
2058 if (offset) {
2059 new_key = key_info[offset];
2060 if (new_key) {
2061 ret = 1;
2062 if (new_key == SPK_KEY) {
2063 if (!spk_key_locked)
2064 key_speakup = (up_flag) ? 0 : 16;
2065 if (up_flag || spk_killed)
2066 goto out;
2067 spk_shut_up &= 0xfe;
2068 do_flush();
2069 goto out;
2070 }
2071 if (up_flag)
2072 goto out;
2073 if (last_keycode == keycode &&
16d35515 2074 last_spk_jiffy + MAX_DELAY > jiffies) {
c6e3fd22 2075 spk_close_press = 1;
16d35515
WH
2076 offset = shift_table[shift_info + 32];
2077 /* double press? */
c6e3fd22
WH
2078 if (offset && key_info[offset])
2079 new_key = key_info[offset];
2080 }
2081 last_keycode = keycode;
2082 last_spk_jiffy = jiffies;
2083 type = KT_SPKUP;
2084 value = new_key;
2085 }
2086 }
2087no_map:
2088 if (type == KT_SPKUP && special_handler == NULL) {
2089 do_spkup(vc, new_key);
2090 spk_close_press = 0;
2091 ret = 1;
2092 goto out;
2093 }
2094 if (up_flag || spk_killed || type == KT_SHIFT)
2095 goto out;
2096 spk_shut_up &= 0xfe;
2097 kh = (value == KVAL(K_DOWN))
16d35515
WH
2098 || (value == KVAL(K_UP))
2099 || (value == KVAL(K_LEFT))
2100 || (value == KVAL(K_RIGHT));
c6e3fd22
WH
2101 if ((cursor_track != read_all_mode) || !kh)
2102 if (!no_intr)
2103 do_flush();
2104 if (special_handler) {
2105 if (type == KT_SPEC && value == 1) {
2106 value = '\n';
2107 type = KT_LATIN;
2108 } else if (type == KT_LETTER)
2109 type = KT_LATIN;
2110 else if (value == 0x7f)
16d35515
WH
2111 value = 8; /* make del = backspace */
2112 ret = (*special_handler) (vc, type, value, keycode);
c6e3fd22
WH
2113 spk_close_press = 0;
2114 if (ret < 0)
2115 bleep(9);
2116 goto out;
2117 }
2118 last_keycode = 0;
2119out:
2120 spk_unlock(flags);
2121 return ret;
2122}
2123
2124static int keyboard_notifier_call(struct notifier_block *nb,
16d35515 2125 unsigned long code, void *_param)
c6e3fd22
WH
2126{
2127 struct keyboard_notifier_param *param = _param;
2128 struct vc_data *vc = param->vc;
2129 int up = !param->down;
2130 int ret = NOTIFY_OK;
16d35515 2131 static int keycode; /* to hold the current keycode */
c6e3fd22
WH
2132
2133 if (vc->vc_mode == KD_GRAPHICS)
2134 return ret;
2135
2136 /*
2137 * First, determine whether we are handling a fake keypress on
2138 * the current processor. If we are, then return NOTIFY_OK,
2139 * to pass the keystroke up the chain. This prevents us from
2140 * trying to take the Speakup lock while it is held by the
2141 * processor on which the simulated keystroke was generated.
2142 * Also, the simulated keystrokes should be ignored by Speakup.
2143 */
2144
2145 if (speakup_fake_key_pressed())
2146 return ret;
2147
2148 switch (code) {
2149 case KBD_KEYCODE:
2150 /* speakup requires keycode and keysym currently */
2151 keycode = param->value;
2152 break;
2153 case KBD_UNBOUND_KEYCODE:
2154 /* not used yet */
2155 break;
2156 case KBD_UNICODE:
2157 /* not used yet */
2158 break;
2159 case KBD_KEYSYM:
2160 if (speakup_key(vc, param->shift, keycode, param->value, up))
2161 ret = NOTIFY_STOP;
16d35515
WH
2162 else if (KTYP(param->value) == KT_CUR)
2163 ret = pre_handle_cursor(vc, KVAL(param->value), up);
c6e3fd22 2164 break;
16d35515
WH
2165 case KBD_POST_KEYSYM:{
2166 unsigned char type = KTYP(param->value) - 0xf0;
2167 unsigned char val = KVAL(param->value);
2168 switch (type) {
2169 case KT_SHIFT:
2170 do_handle_shift(vc, val, up);
2171 break;
2172 case KT_LATIN:
2173 case KT_LETTER:
2174 do_handle_latin(vc, val, up);
2175 break;
2176 case KT_CUR:
2177 do_handle_cursor(vc, val, up);
2178 break;
2179 case KT_SPEC:
2180 do_handle_spec(vc, val, up);
2181 break;
2182 }
c6e3fd22
WH
2183 break;
2184 }
c6e3fd22
WH
2185 }
2186 return ret;
2187}
2188
2189static int vt_notifier_call(struct notifier_block *nb,
16d35515 2190 unsigned long code, void *_param)
c6e3fd22
WH
2191{
2192 struct vt_notifier_param *param = _param;
2193 struct vc_data *vc = param->vc;
2194 switch (code) {
2195 case VT_ALLOCATE:
2196 if (vc->vc_mode == KD_TEXT)
2197 speakup_allocate(vc);
2198 break;
2199 case VT_DEALLOCATE:
2200 speakup_deallocate(vc);
2201 break;
2202 case VT_WRITE:
2203 if (param->c == '\b')
2204 speakup_bs(vc);
16d35515
WH
2205 else if (param->c < 0x100) {
2206 char d = param->c;
2207 speakup_con_write(vc, &d, 1);
2208 }
c6e3fd22
WH
2209 break;
2210 case VT_UPDATE:
2211 speakup_con_update(vc);
2212 break;
2213 }
2214 return NOTIFY_OK;
2215}
2216
2217/* called by: module_exit() */
2218static void __exit speakup_exit(void)
2219{
2220 int i;
2221
c6e3fd22
WH
2222 unregister_keyboard_notifier(&keyboard_notifier_block);
2223 unregister_vt_notifier(&vt_notifier_block);
2224 speakup_unregister_devsynth();
2225 del_timer(&cursor_timer);
c6e3fd22
WH
2226 kthread_stop(speakup_task);
2227 speakup_task = NULL;
2228 mutex_lock(&spk_mutex);
2229 synth_release();
2230 mutex_unlock(&spk_mutex);
2231
628f3428
CB
2232 speakup_kobj_exit();
2233
2234 for (i = 0; i < MAX_NR_CONSOLES; i++)
2235 kfree(speakup_console[i]);
2236
2237 speakup_remove_virtual_keyboard();
2238
c6e3fd22
WH
2239 for (i = 0; i < MAXVARS; i++)
2240 speakup_unregister_var(i);
2241
2242 for (i = 0; i < 256; i++) {
2243 if (characters[i] != default_chars[i])
2244 kfree(characters[i]);
2245 }
628f3428
CB
2246
2247 free_user_msgs();
c6e3fd22
WH
2248}
2249
2250/* call by: module_init() */
2251static int __init speakup_init(void)
2252{
2253 int i;
628f3428 2254 long err = 0;
c6e3fd22
WH
2255 struct st_spk_t *first_console;
2256 struct vc_data *vc = vc_cons[fg_console].d;
2257 struct var_t *var;
2258
628f3428 2259 /* These first few initializations cannot fail. */
16d35515 2260 initialize_msgs(); /* Initialize arrays for i18n. */
c6e3fd22
WH
2261 reset_default_chars();
2262 reset_default_chartab();
c6e3fd22
WH
2263 strlwr(synth_name);
2264 spk_vars[0].u.n.high = vc->vc_cols;
16d35515 2265 for (var = spk_vars; var->var_id != MAXVARS; var++)
c6e3fd22 2266 speakup_register_var(var);
16d35515
WH
2267 for (var = synth_time_vars;
2268 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
c6e3fd22
WH
2269 speakup_register_var(var);
2270 for (i = 1; punc_info[i].mask != 0; i++)
2271 set_mask_bits(0, i, 2);
2272
2273 set_key_info(key_defaults, key_buf);
2274 if (quiet_boot)
2275 spk_shut_up |= 0x01;
2276
628f3428
CB
2277 /* From here on out, initializations can fail. */
2278 err = speakup_add_virtual_keyboard();
2279 if (err)
2280 goto error_virtkeyboard;
2281
2282 first_console = kzalloc(sizeof(*first_console), GFP_KERNEL);
2283 if (!first_console) {
2284 err = -ENOMEM;
2285 goto error_alloc;
2286 }
2287
2288 speakup_console[vc->vc_num] = first_console;
2289 speakup_date(vc);
2290
c6e3fd22 2291 for (i = 0; i < MAX_NR_CONSOLES; i++)
628f3428
CB
2292 if (vc_cons[i].d) {
2293 err = speakup_allocate(vc_cons[i].d);
2294 if (err)
2295 goto error_kobjects;
2296 }
2297
2298 err = speakup_kobj_init();
2299 if (err)
2300 goto error_kobjects;
c6e3fd22 2301
c6e3fd22
WH
2302 synth_init(synth_name);
2303 speakup_register_devsynth();
628f3428
CB
2304 /*
2305 * register_devsynth might fail, but this error is not fatal.
2306 * /dev/synth is an extra feature; the rest of Speakup
2307 * will work fine without it.
2308 */
c6e3fd22 2309
628f3428
CB
2310 err = register_keyboard_notifier(&keyboard_notifier_block);
2311 if (err)
2312 goto error_kbdnotifier;
2313 err = register_vt_notifier(&vt_notifier_block);
2314 if (err)
2315 goto error_vtnotifier;
c6e3fd22
WH
2316
2317 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
628f3428 2318
7959d556 2319 if (IS_ERR(speakup_task)) {
628f3428
CB
2320 err = PTR_ERR(speakup_task);
2321 goto error_task;
7959d556 2322 }
628f3428
CB
2323
2324 set_user_nice(speakup_task, 10);
7959d556 2325 wake_up_process(speakup_task);
628f3428
CB
2326
2327 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2328 pr_info("synth name on entry is: %s\n", synth_name);
7959d556
WH
2329 goto out;
2330
628f3428
CB
2331error_task:
2332 unregister_vt_notifier(&vt_notifier_block);
2333
2334error_vtnotifier:
2335 unregister_keyboard_notifier(&keyboard_notifier_block);
2336 del_timer(&cursor_timer);
2337
2338error_kbdnotifier:
2339 speakup_unregister_devsynth();
2340 mutex_lock(&spk_mutex);
2341 synth_release();
2342 mutex_unlock(&spk_mutex);
2343 speakup_kobj_exit();
2344
2345error_kobjects:
2346 for (i = 0; i < MAX_NR_CONSOLES; i++)
2347 kfree(speakup_console[i]);
2348
2349error_alloc:
7959d556 2350 speakup_remove_virtual_keyboard();
628f3428
CB
2351
2352error_virtkeyboard:
2353 for (i = 0; i < MAXVARS; i++)
2354 speakup_unregister_var(i);
2355
2356 for (i = 0; i < 256; i++) {
2357 if (characters[i] != default_chars[i])
2358 kfree(characters[i]);
2359 }
2360
2361 free_user_msgs();
2362
7959d556
WH
2363out:
2364 return err;
c6e3fd22
WH
2365}
2366
c6e3fd22
WH
2367module_init(speakup_init);
2368module_exit(speakup_exit);