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