]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/staging/speakup/main.c
Merge tag 'powerpc-4.13-8' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[mirror_ubuntu-artful-kernel.git] / drivers / staging / speakup / main.c
CommitLineData
c6e3fd22 1/* speakup.c
16d35515
WH
2 * review functions for the speakup screen review package.
3 * originally written by: Kirk Reiser and Andy Berdan.
4 *
5 * extensively modified by David Borowski.
6 *
7 ** Copyright (C) 1998 Kirk Reiser.
8 * Copyright (C) 2003 David Borowski.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
4d0bdcb1 19 */
c6e3fd22
WH
20
21#include <linux/kernel.h>
c6e3fd22
WH
22#include <linux/vt.h>
23#include <linux/tty.h>
16d35515 24#include <linux/mm.h> /* __get_free_page() and friends */
c6e3fd22
WH
25#include <linux/vt_kern.h>
26#include <linux/ctype.h>
27#include <linux/selection.h>
28#include <linux/unistd.h>
29#include <linux/jiffies.h>
30#include <linux/kthread.h>
31#include <linux/keyboard.h> /* for KT_SHIFT */
16d35515 32#include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
c6e3fd22
WH
33#include <linux/input.h>
34#include <linux/kmod.h>
35
c6e3fd22
WH
36/* speakup_*_selection */
37#include <linux/module.h>
38#include <linux/sched.h>
39#include <linux/slab.h>
40#include <linux/types.h>
41#include <linux/consolemap.h>
42
43#include <linux/spinlock.h>
44#include <linux/notifier.h>
45
16d35515 46#include <linux/uaccess.h> /* copy_from|to|user() and others */
c6e3fd22
WH
47
48#include "spk_priv.h"
49#include "speakup.h"
50
51#define MAX_DELAY msecs_to_jiffies(500)
52#define MINECHOCHAR SPACE
53
54MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
55MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
56MODULE_DESCRIPTION("Speakup console speech");
57MODULE_LICENSE("GPL");
58MODULE_VERSION(SPEAKUP_VERSION);
59
60char *synth_name;
73c3700e
DR
61module_param_named(synth, synth_name, charp, 0444);
62module_param_named(quiet, spk_quiet_boot, bool, 0444);
c6e3fd22
WH
63
64MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
65MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
66
ca2beaf8 67special_func spk_special_handler;
c6e3fd22 68
ca2beaf8 69short spk_pitch_shift, synth_flags;
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) {
c6e3fd22
WH
450 pr_info("speak_char: cp == NULL!\n");
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);
1167static void cursor_done(u_long data);
1168static DEFINE_TIMER(cursor_timer, cursor_done, 0, 0);
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
c6e3fd22
WH
1379static void start_read_all_timer(struct vc_data *vc, int command);
1380
1381enum {
1382 RA_NOTHING,
1383 RA_NEXT_SENT,
1384 RA_PREV_LINE,
1385 RA_NEXT_LINE,
1386 RA_PREV_SENT,
1387 RA_DOWN_ARROW,
1388 RA_TIMER,
1389 RA_FIND_NEXT_SENT,
1390 RA_FIND_PREV_SENT,
1391};
1392
16d35515 1393static void kbd_fakekey2(struct vc_data *vc, int command)
c6e3fd22
WH
1394{
1395 del_timer(&cursor_timer);
1396 speakup_fake_down_arrow();
1397 start_read_all_timer(vc, command);
1398}
1399
16d35515 1400static void read_all_doc(struct vc_data *vc)
c6e3fd22 1401{
0d6ff616 1402 if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
c6e3fd22
WH
1403 return;
1404 if (!synth_supports_indexing())
1405 return;
1406 if (cursor_track != read_all_mode)
1407 prev_cursor_track = cursor_track;
1408 cursor_track = read_all_mode;
ca2beaf8 1409 spk_reset_index_count(0);
0dcb2124 1410 if (get_sentence_buf(vc, 0) == -1) {
c6e3fd22 1411 kbd_fakekey2(vc, RA_DOWN_ARROW);
0dcb2124 1412 } else {
c6e3fd22
WH
1413 say_sentence_num(0, 0);
1414 synth_insert_next_index(0);
1415 start_read_all_timer(vc, RA_TIMER);
1416 }
1417}
1418
16d35515 1419static void stop_read_all(struct vc_data *vc)
c6e3fd22
WH
1420{
1421 del_timer(&cursor_timer);
1422 cursor_track = prev_cursor_track;
1423 spk_shut_up &= 0xfe;
ca2beaf8 1424 spk_do_flush();
c6e3fd22
WH
1425}
1426
16d35515 1427static void start_read_all_timer(struct vc_data *vc, int command)
c6e3fd22
WH
1428{
1429 struct var_t *cursor_timeout;
1430
1431 cursor_con = vc->vc_num;
1432 read_all_key = command;
ca2beaf8 1433 cursor_timeout = spk_get_var(CURSOR_TIME);
16d35515
WH
1434 mod_timer(&cursor_timer,
1435 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
c6e3fd22
WH
1436}
1437
16d35515 1438static void handle_cursor_read_all(struct vc_data *vc, int command)
c6e3fd22
WH
1439{
1440 int indcount, sentcount, rv, sn;
1441
1442 switch (command) {
1443 case RA_NEXT_SENT:
1444 /* Get Current Sentence */
ca2beaf8 1445 spk_get_index_count(&indcount, &sentcount);
c6e3fd22 1446 /*printk("%d %d ", indcount, sentcount); */
ca2beaf8 1447 spk_reset_index_count(sentcount + 1);
c6e3fd22 1448 if (indcount == 1) {
16d35515 1449 if (!say_sentence_num(sentcount + 1, 0)) {
c6e3fd22
WH
1450 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1451 return;
1452 }
1453 synth_insert_next_index(0);
1454 } else {
1455 sn = 0;
16d35515 1456 if (!say_sentence_num(sentcount + 1, 1)) {
c6e3fd22 1457 sn = 1;
ca2beaf8 1458 spk_reset_index_count(sn);
0dcb2124 1459 } else {
c6e3fd22 1460 synth_insert_next_index(0);
0dcb2124 1461 }
c6e3fd22
WH
1462 if (!say_sentence_num(sn, 0)) {
1463 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1464 return;
1465 }
1466 synth_insert_next_index(0);
1467 }
1468 start_read_all_timer(vc, RA_TIMER);
1469 break;
1470 case RA_PREV_SENT:
1471 break;
1472 case RA_NEXT_LINE:
1473 read_all_doc(vc);
1474 break;
1475 case RA_PREV_LINE:
1476 break;
1477 case RA_DOWN_ARROW:
1478 if (get_sentence_buf(vc, 0) == -1) {
1479 kbd_fakekey2(vc, RA_DOWN_ARROW);
1480 } else {
1481 say_sentence_num(0, 0);
1482 synth_insert_next_index(0);
1483 start_read_all_timer(vc, RA_TIMER);
1484 }
1485 break;
1486 case RA_FIND_NEXT_SENT:
1487 rv = get_sentence_buf(vc, 0);
1488 if (rv == -1)
1489 read_all_doc(vc);
0dcb2124 1490 if (rv == 0) {
c6e3fd22 1491 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
0dcb2124 1492 } else {
c6e3fd22
WH
1493 say_sentence_num(1, 0);
1494 synth_insert_next_index(0);
1495 start_read_all_timer(vc, RA_TIMER);
1496 }
1497 break;
1498 case RA_FIND_PREV_SENT:
1499 break;
1500 case RA_TIMER:
ca2beaf8 1501 spk_get_index_count(&indcount, &sentcount);
c6e3fd22
WH
1502 if (indcount < 2)
1503 kbd_fakekey2(vc, RA_DOWN_ARROW);
1504 else
1505 start_read_all_timer(vc, RA_TIMER);
1506 break;
1507 }
1508}
1509
1510static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1511{
1512 unsigned long flags;
8e69a811 1513
3efe810f 1514 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1515 if (cursor_track == read_all_mode) {
1516 spk_parked &= 0xfe;
0d6ff616 1517 if (!synth || up_flag || spk_shut_up) {
3efe810f 1518 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1519 return NOTIFY_STOP;
1520 }
1521 del_timer(&cursor_timer);
1522 spk_shut_up &= 0xfe;
ca2beaf8 1523 spk_do_flush();
16d35515 1524 start_read_all_timer(vc, value + 1);
3efe810f 1525 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1526 return NOTIFY_STOP;
1527 }
3efe810f 1528 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1529 return NOTIFY_OK;
1530}
1531
1532static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1533{
1534 unsigned long flags;
1535 struct var_t *cursor_timeout;
1536
3efe810f 1537 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22 1538 spk_parked &= 0xfe;
0d6ff616 1539 if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
3efe810f 1540 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1541 return;
1542 }
1543 spk_shut_up &= 0xfe;
ca2beaf8
ST
1544 if (spk_no_intr)
1545 spk_do_flush();
c6e3fd22 1546/* the key press flushes if !no_inter but we want to flush on cursor
13d825ed
AF
1547 * moves regardless of no_inter state
1548 */
c6e3fd22
WH
1549 is_cursor = value + 1;
1550 old_cursor_pos = vc->vc_pos;
1551 old_cursor_x = vc->vc_x;
1552 old_cursor_y = vc->vc_y;
1553 speakup_console[vc->vc_num]->ht.cy = vc->vc_y;
1554 cursor_con = vc->vc_num;
1555 if (cursor_track == CT_Highlight)
1556 reset_highlight_buffers(vc);
ca2beaf8 1557 cursor_timeout = spk_get_var(CURSOR_TIME);
16d35515
WH
1558 mod_timer(&cursor_timer,
1559 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
3efe810f 1560 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1561}
1562
9831013c 1563static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
c6e3fd22
WH
1564{
1565 int i, bi, hi;
1566 int vc_num = vc->vc_num;
1567
6de3f58b 1568 bi = (vc->vc_attr & 0x70) >> 4;
c6e3fd22
WH
1569 hi = speakup_console[vc_num]->ht.highsize[bi];
1570
1571 i = 0;
1572 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1573 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1574 speakup_console[vc_num]->ht.rx[bi] = vc->vc_x;
1575 speakup_console[vc_num]->ht.ry[bi] = vc->vc_y;
1576 }
1577 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
9831013c 1578 if (ic[i] > 32) {
c6e3fd22
WH
1579 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1580 hi++;
1581 } else if ((ic[i] == 32) && (hi != 0)) {
16d35515
WH
1582 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1583 32) {
c6e3fd22 1584 speakup_console[vc_num]->ht.highbuf[bi][hi] =
16d35515 1585 ic[i];
c6e3fd22
WH
1586 hi++;
1587 }
1588 }
1589 i++;
1590 }
1591 speakup_console[vc_num]->ht.highsize[bi] = hi;
1592}
1593
16d35515 1594static void reset_highlight_buffers(struct vc_data *vc)
c6e3fd22
WH
1595{
1596 int i;
1597 int vc_num = vc->vc_num;
8e69a811 1598
16d35515 1599 for (i = 0; i < 8; i++)
c6e3fd22
WH
1600 speakup_console[vc_num]->ht.highsize[i] = 0;
1601}
1602
16d35515 1603static int count_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1604{
1605 int i, bg;
1606 int cc;
1607 int vc_num = vc->vc_num;
1608 u16 ch;
d290effe 1609 u16 *start = (u16 *)vc->vc_origin;
c6e3fd22
WH
1610
1611 for (i = 0; i < 8; i++)
1612 speakup_console[vc_num]->ht.bgcount[i] = 0;
1613
1614 for (i = 0; i < vc->vc_rows; i++) {
16d35515 1615 u16 *end = start + vc->vc_cols * 2;
c6e3fd22 1616 u16 *ptr;
8e69a811 1617
c6e3fd22 1618 for (ptr = start; ptr < end; ptr++) {
88867e3d 1619 ch = get_attributes(vc, ptr);
c6e3fd22
WH
1620 bg = (ch & 0x70) >> 4;
1621 speakup_console[vc_num]->ht.bgcount[bg]++;
1622 }
1623 start += vc->vc_size_row;
1624 }
1625
1626 cc = 0;
1627 for (i = 0; i < 8; i++)
1628 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1629 cc++;
1630 return cc;
1631}
1632
16d35515 1633static int get_highlight_color(struct vc_data *vc)
c6e3fd22
WH
1634{
1635 int i, j;
590aeb17 1636 unsigned int cptr[8];
c6e3fd22
WH
1637 int vc_num = vc->vc_num;
1638
1639 for (i = 0; i < 8; i++)
1640 cptr[i] = i;
1641
1642 for (i = 0; i < 7; i++)
1643 for (j = i + 1; j < 8; j++)
1644 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
590aeb17
FF
1645 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1646 swap(cptr[i], cptr[j]);
c6e3fd22
WH
1647
1648 for (i = 0; i < 8; i++)
1649 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1650 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1651 return cptr[i];
1652 return -1;
1653}
1654
16d35515 1655static int speak_highlight(struct vc_data *vc)
c6e3fd22
WH
1656{
1657 int hc, d;
1658 int vc_num = vc->vc_num;
8e69a811 1659
c6e3fd22
WH
1660 if (count_highlight_color(vc) == 1)
1661 return 0;
1662 hc = get_highlight_color(vc);
1663 if (hc != -1) {
16d35515 1664 d = vc->vc_y - speakup_console[vc_num]->ht.cy;
c6e3fd22
WH
1665 if ((d == 1) || (d == -1))
1666 if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y)
1667 return 0;
1668 spk_parked |= 0x01;
ca2beaf8 1669 spk_do_flush();
c6e3fd22 1670 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
16d35515 1671 speakup_console[vc_num]->ht.highsize[hc]);
c6e3fd22
WH
1672 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1673 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1674 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1675 return 1;
1676 }
1677 return 0;
1678}
1679
16d35515 1680static void cursor_done(u_long data)
c6e3fd22
WH
1681{
1682 struct vc_data *vc = vc_cons[cursor_con].d;
1683 unsigned long flags;
8e69a811 1684
c6e3fd22 1685 del_timer(&cursor_timer);
3efe810f 1686 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22
WH
1687 if (cursor_con != fg_console) {
1688 is_cursor = 0;
1689 goto out;
1690 }
1691 speakup_date(vc);
1692 if (win_enabled) {
1693 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1694 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
cd95ed98
AS
1695 spk_keydown = 0;
1696 is_cursor = 0;
c6e3fd22
WH
1697 goto out;
1698 }
1699 }
1700 if (cursor_track == read_all_mode) {
1701 handle_cursor_read_all(vc, read_all_key);
1702 goto out;
1703 }
1704 if (cursor_track == CT_Highlight) {
1705 if (speak_highlight(vc)) {
cd95ed98
AS
1706 spk_keydown = 0;
1707 is_cursor = 0;
c6e3fd22
WH
1708 goto out;
1709 }
1710 }
1711 if (cursor_track == CT_Window)
1712 speakup_win_say(vc);
1713 else if (is_cursor == 1 || is_cursor == 4)
1714 say_line_from_to(vc, 0, vc->vc_cols, 0);
1715 else
1716 say_char(vc);
cd95ed98
AS
1717 spk_keydown = 0;
1718 is_cursor = 0;
c6e3fd22 1719out:
3efe810f 1720 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1721}
1722
1723/* called by: vt_notifier_call() */
1724static void speakup_bs(struct vc_data *vc)
1725{
1726 unsigned long flags;
8e69a811 1727
c6e3fd22
WH
1728 if (!speakup_console[vc->vc_num])
1729 return;
3efe810f 1730 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1731 /* Speakup output, discard */
1732 return;
1733 if (!spk_parked)
1734 speakup_date(vc);
0d6ff616 1735 if (spk_shut_up || !synth) {
3efe810f 1736 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1737 return;
1738 }
1739 if (vc->vc_num == fg_console && spk_keydown) {
1740 spk_keydown = 0;
1741 if (!is_cursor)
1742 say_char(vc);
1743 }
3efe810f 1744 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1745}
1746
1747/* called by: vt_notifier_call() */
9831013c 1748static void speakup_con_write(struct vc_data *vc, u16 *str, int len)
c6e3fd22
WH
1749{
1750 unsigned long flags;
8e69a811 1751
0d6ff616 1752 if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
c6e3fd22 1753 return;
3efe810f 1754 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1755 /* Speakup output, discard */
1756 return;
ca2beaf8 1757 if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1))
c6e3fd22
WH
1758 bleep(3);
1759 if ((is_cursor) || (cursor_track == read_all_mode)) {
1760 if (cursor_track == CT_Highlight)
1761 update_color_buffer(vc, str, len);
3efe810f 1762 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1763 return;
1764 }
1765 if (win_enabled) {
1766 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
16d35515 1767 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
3efe810f 1768 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1769 return;
1770 }
1771 }
1772
1773 spkup_write(str, len);
3efe810f 1774 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1775}
1776
0012196c 1777static void speakup_con_update(struct vc_data *vc)
c6e3fd22
WH
1778{
1779 unsigned long flags;
8e69a811 1780
0d6ff616 1781 if (!speakup_console[vc->vc_num] || spk_parked)
c6e3fd22 1782 return;
3efe810f 1783 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
c6e3fd22
WH
1784 /* Speakup output, discard */
1785 return;
1786 speakup_date(vc);
3efe810f 1787 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1788}
1789
1790static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1791{
1792 unsigned long flags;
1793 int on_off = 2;
1794 char *label;
8e69a811 1795
0d6ff616 1796 if (!synth || up_flag || spk_killed)
c6e3fd22 1797 return;
3efe810f 1798 spin_lock_irqsave(&speakup_info.spinlock, flags);
c6e3fd22 1799 spk_shut_up &= 0xfe;
ca2beaf8
ST
1800 if (spk_no_intr)
1801 spk_do_flush();
c6e3fd22
WH
1802 switch (value) {
1803 case KVAL(K_CAPS):
ca2beaf8 1804 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
079c9534 1805 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
c6e3fd22
WH
1806 break;
1807 case KVAL(K_NUM):
ca2beaf8 1808 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
079c9534 1809 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
c6e3fd22
WH
1810 break;
1811 case KVAL(K_HOLD):
ca2beaf8 1812 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
079c9534 1813 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
c6e3fd22
WH
1814 if (speakup_console[vc->vc_num])
1815 speakup_console[vc->vc_num]->tty_stopped = on_off;
1816 break;
1817 default:
1818 spk_parked &= 0xfe;
3efe810f 1819 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1820 return;
1821 }
1822 if (on_off < 2)
1823 synth_printf("%s %s\n",
ca2beaf8 1824 label, spk_msg_get(MSG_STATUS_START + on_off));
3efe810f 1825 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
1826}
1827
16d35515 1828static int inc_dec_var(u_char value)
c6e3fd22
WH
1829{
1830 struct st_var_header *p_header;
1831 struct var_t *var_data;
1832 char num_buf[32];
1833 char *cp = num_buf;
1834 char *pn;
1835 int var_id = (int)value - VAR_START;
16d35515 1836 int how = (var_id & 1) ? E_INC : E_DEC;
8e69a811 1837
16d35515 1838 var_id = var_id / 2 + FIRST_SET_VAR;
ca2beaf8 1839 p_header = spk_get_var_header(var_id);
0d6ff616 1840 if (!p_header)
c6e3fd22
WH
1841 return -1;
1842 if (p_header->var_type != VAR_NUM)
1843 return -1;
1844 var_data = p_header->data;
ca2beaf8 1845 if (spk_set_num_var(1, p_header, how) != 0)
c6e3fd22
WH
1846 return -1;
1847 if (!spk_close_press) {
1848 for (pn = p_header->name; *pn; pn++) {
1849 if (*pn == '_')
1850 *cp = SPACE;
1851 else
1852 *cp++ = *pn;
1853 }
1854 }
1855 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
16d35515 1856 var_data->u.n.value);
c6e3fd22
WH
1857 synth_printf("%s", num_buf);
1858 return 0;
1859}
1860
16d35515 1861static void speakup_win_set(struct vc_data *vc)
c6e3fd22
WH
1862{
1863 char info[40];
8e69a811 1864
c6e3fd22 1865 if (win_start > 1) {
ca2beaf8 1866 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
c6e3fd22
WH
1867 return;
1868 }
1869 if (spk_x < win_left || spk_y < win_top) {
ca2beaf8 1870 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
c6e3fd22
WH
1871 return;
1872 }
1873 if (win_start && spk_x == win_left && spk_y == win_top) {
1874 win_left = 0;
16d35515 1875 win_right = vc->vc_cols - 1;
c6e3fd22 1876 win_bottom = spk_y;
ca2beaf8 1877 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
16d35515 1878 (int)win_top + 1);
c6e3fd22
WH
1879 } else {
1880 if (!win_start) {
1881 win_top = spk_y;
1882 win_left = spk_x;
1883 } else {
1884 win_bottom = spk_y;
1885 win_right = spk_x;
1886 }
ca2beaf8 1887 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
63b8ebe4
SG
1888 (win_start) ?
1889 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
16d35515 1890 (int)spk_y + 1, (int)spk_x + 1);
c6e3fd22
WH
1891 }
1892 synth_printf("%s\n", info);
1893 win_start++;
1894}
1895
16d35515 1896static void speakup_win_clear(struct vc_data *vc)
c6e3fd22 1897{
cd95ed98
AS
1898 win_top = 0;
1899 win_bottom = 0;
1900 win_left = 0;
1901 win_right = 0;
c6e3fd22 1902 win_start = 0;
ca2beaf8 1903 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
c6e3fd22
WH
1904}
1905
16d35515 1906static void speakup_win_enable(struct vc_data *vc)
c6e3fd22
WH
1907{
1908 if (win_start < 2) {
ca2beaf8 1909 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
c6e3fd22
WH
1910 return;
1911 }
1912 win_enabled ^= 1;
1913 if (win_enabled)
ca2beaf8 1914 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
c6e3fd22 1915 else
ca2beaf8 1916 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
c6e3fd22
WH
1917}
1918
16d35515 1919static void speakup_bits(struct vc_data *vc)
c6e3fd22
WH
1920{
1921 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
8e69a811 1922
0d6ff616 1923 if (spk_special_handler || val < 1 || val > 6) {
ca2beaf8 1924 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
c6e3fd22
WH
1925 return;
1926 }
ca2beaf8
ST
1927 pb_edit = &spk_punc_info[val];
1928 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1929 spk_special_handler = edit_bits;
c6e3fd22
WH
1930}
1931
1932static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1933{
4ea418b8 1934 static u_char goto_buf[8];
16d35515 1935 static int num;
ef35a4f4 1936 int maxlen;
c6e3fd22 1937 char *cp;
9831013c 1938 u16 wch;
ef35a4f4 1939
c6e3fd22
WH
1940 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1941 goto do_goto;
1942 if (type == KT_LATIN && ch == '\n')
1943 goto do_goto;
1944 if (type != 0)
1945 goto oops;
1946 if (ch == 8) {
9831013c 1947 u16 wch;
650b175d 1948
c6e3fd22
WH
1949 if (num == 0)
1950 return -1;
9831013c 1951 wch = goto_buf[--num];
c6e3fd22 1952 goto_buf[num] = '\0';
9831013c 1953 spkup_write(&wch, 1);
c6e3fd22
WH
1954 return 1;
1955 }
1956 if (ch < '+' || ch > 'y')
1957 goto oops;
9831013c 1958 wch = ch;
c6e3fd22
WH
1959 goto_buf[num++] = ch;
1960 goto_buf[num] = '\0';
9831013c 1961 spkup_write(&wch, 1);
c6e3fd22
WH
1962 maxlen = (*goto_buf >= '0') ? 3 : 4;
1963 if ((ch == '+' || ch == '-') && num == 1)
1964 return 1;
1965 if (ch >= '0' && ch <= '9' && num < maxlen)
1966 return 1;
16d35515 1967 if (num < maxlen - 1 || num > maxlen)
c6e3fd22
WH
1968 goto oops;
1969 if (ch < 'x' || ch > 'y') {
1970oops:
1971 if (!spk_killed)
ca2beaf8 1972 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
c6e3fd22 1973 goto_buf[num = 0] = '\0';
ca2beaf8 1974 spk_special_handler = NULL;
c6e3fd22
WH
1975 return 1;
1976 }
ef35a4f4
DY
1977
1978 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1979
c6e3fd22
WH
1980 if (*cp == 'x') {
1981 if (*goto_buf < '0')
1982 goto_pos += spk_x;
ef35a4f4 1983 else if (goto_pos > 0)
c6e3fd22 1984 goto_pos--;
ef35a4f4 1985
c6e3fd22 1986 if (goto_pos >= vc->vc_cols)
16d35515 1987 goto_pos = vc->vc_cols - 1;
c6e3fd22
WH
1988 goto_x = 1;
1989 } else {
1990 if (*goto_buf < '0')
1991 goto_pos += spk_y;
ef35a4f4 1992 else if (goto_pos > 0)
c6e3fd22 1993 goto_pos--;
ef35a4f4 1994
c6e3fd22 1995 if (goto_pos >= vc->vc_rows)
16d35515 1996 goto_pos = vc->vc_rows - 1;
c6e3fd22
WH
1997 goto_x = 0;
1998 }
16d35515 1999 goto_buf[num = 0] = '\0';
c6e3fd22 2000do_goto:
ca2beaf8 2001 spk_special_handler = NULL;
c6e3fd22
WH
2002 spk_parked |= 0x01;
2003 if (goto_x) {
2004 spk_pos -= spk_x * 2;
2005 spk_x = goto_pos;
2006 spk_pos += goto_pos * 2;
2007 say_word(vc);
2008 } else {
2009 spk_y = goto_pos;
2010 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
2011 say_line(vc);
2012 }
2013 return 1;
2014}
2015
16d35515 2016static void speakup_goto(struct vc_data *vc)
c6e3fd22 2017{
0d6ff616 2018 if (spk_special_handler) {
ca2beaf8 2019 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
c6e3fd22
WH
2020 return;
2021 }
ca2beaf8
ST
2022 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
2023 spk_special_handler = handle_goto;
c6e3fd22
WH
2024}
2025
2026static void speakup_help(struct vc_data *vc)
2027{
ca2beaf8 2028 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
c6e3fd22
WH
2029}
2030
16d35515 2031static void do_nothing(struct vc_data *vc)
c6e3fd22 2032{
16d35515 2033 return; /* flush done in do_spkup */
c6e3fd22 2034}
16d35515 2035
c6e3fd22
WH
2036static u_char key_speakup, spk_key_locked;
2037
16d35515 2038static void speakup_lock(struct vc_data *vc)
c6e3fd22 2039{
cd95ed98
AS
2040 if (!spk_key_locked) {
2041 spk_key_locked = 16;
2042 key_speakup = 16;
2043 } else {
2044 spk_key_locked = 0;
2045 key_speakup = 0;
2046 }
c6e3fd22
WH
2047}
2048
16d35515 2049typedef void (*spkup_hand) (struct vc_data *);
0012196c 2050static spkup_hand spkup_handler[] = {
c6e3fd22
WH
2051 /* must be ordered same as defines in speakup.h */
2052 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2053 speakup_cut, speakup_paste, say_first_char, say_last_char,
2054 say_char, say_prev_char, say_next_char,
2055 say_word, say_prev_word, say_next_word,
2056 say_line, say_prev_line, say_next_line,
2057 top_edge, bottom_edge, left_edge, right_edge,
2058 spell_word, spell_word, say_screen,
2059 say_position, say_attributes,
16d35515 2060 speakup_off, speakup_parked, say_line, /* this is for indent */
c6e3fd22
WH
2061 say_from_top, say_to_bottom,
2062 say_from_left, say_to_right,
2063 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2064 speakup_bits, speakup_bits, speakup_bits,
2065 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2066 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2067};
2068
2069static void do_spkup(struct vc_data *vc, u_char value)
2070{
2071 if (spk_killed && value != SPEECH_KILL)
2072 return;
2073 spk_keydown = 0;
2074 spk_lastkey = 0;
2075 spk_shut_up &= 0xfe;
2076 this_speakup_key = value;
2077 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
ca2beaf8 2078 spk_do_flush();
16d35515 2079 (*spkup_handler[value]) (vc);
c6e3fd22
WH
2080 } else {
2081 if (inc_dec_var(value) < 0)
2082 bleep(9);
2083 }
2084}
2085
2086static const char *pad_chars = "0123456789+-*/\015,.?()";
2087
0012196c 2088static int
c6e3fd22 2089speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
16d35515 2090 int up_flag)
c6e3fd22
WH
2091{
2092 unsigned long flags;
2093 int kh;
2094 u_char *key_info;
2095 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2096 u_char shift_info, offset;
2097 int ret = 0;
8e69a811 2098
c6e3fd22
WH
2099 if (synth == NULL)
2100 return 0;
2101
3efe810f 2102 spin_lock_irqsave(&speakup_info.spinlock, flags);
5b19208a 2103 tty = vc->port.tty;
c6e3fd22
WH
2104 if (type >= 0xf0)
2105 type -= 0xf0;
049aa515
AS
2106 if (type == KT_PAD &&
2107 (vt_get_leds(fg_console, VC_NUMLOCK))) {
c6e3fd22
WH
2108 if (up_flag) {
2109 spk_keydown = 0;
2110 goto out;
2111 }
2112 value = spk_lastkey = pad_chars[value];
2113 spk_keydown++;
2114 spk_parked &= 0xfe;
2115 goto no_map;
2116 }
2117 if (keycode >= MAX_KEY)
2118 goto no_map;
ca2beaf8 2119 key_info = spk_our_keys[keycode];
ff471ea8 2120 if (!key_info)
c6e3fd22
WH
2121 goto no_map;
2122 /* Check valid read all mode keys */
2123 if ((cursor_track == read_all_mode) && (!up_flag)) {
2124 switch (value) {
2125 case KVAL(K_DOWN):
2126 case KVAL(K_UP):
2127 case KVAL(K_LEFT):
2128 case KVAL(K_RIGHT):
2129 case KVAL(K_PGUP):
2130 case KVAL(K_PGDN):
2131 break;
2132 default:
2133 stop_read_all(vc);
2134 break;
2135 }
2136 }
16d35515 2137 shift_info = (shift_state & 0x0f) + key_speakup;
ca2beaf8 2138 offset = spk_shift_table[shift_info];
c6e3fd22
WH
2139 if (offset) {
2140 new_key = key_info[offset];
2141 if (new_key) {
2142 ret = 1;
2143 if (new_key == SPK_KEY) {
2144 if (!spk_key_locked)
2145 key_speakup = (up_flag) ? 0 : 16;
2146 if (up_flag || spk_killed)
2147 goto out;
2148 spk_shut_up &= 0xfe;
ca2beaf8 2149 spk_do_flush();
c6e3fd22
WH
2150 goto out;
2151 }
2152 if (up_flag)
2153 goto out;
2154 if (last_keycode == keycode &&
3d3cb1bf 2155 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
c6e3fd22 2156 spk_close_press = 1;
ca2beaf8 2157 offset = spk_shift_table[shift_info + 32];
16d35515 2158 /* double press? */
c6e3fd22
WH
2159 if (offset && key_info[offset])
2160 new_key = key_info[offset];
2161 }
2162 last_keycode = keycode;
2163 last_spk_jiffy = jiffies;
2164 type = KT_SPKUP;
2165 value = new_key;
2166 }
2167 }
2168no_map:
0d6ff616 2169 if (type == KT_SPKUP && !spk_special_handler) {
c6e3fd22
WH
2170 do_spkup(vc, new_key);
2171 spk_close_press = 0;
2172 ret = 1;
2173 goto out;
2174 }
2175 if (up_flag || spk_killed || type == KT_SHIFT)
2176 goto out;
2177 spk_shut_up &= 0xfe;
23996ee5
AS
2178 kh = (value == KVAL(K_DOWN)) ||
2179 (value == KVAL(K_UP)) ||
2180 (value == KVAL(K_LEFT)) ||
2181 (value == KVAL(K_RIGHT));
c6e3fd22 2182 if ((cursor_track != read_all_mode) || !kh)
ca2beaf8
ST
2183 if (!spk_no_intr)
2184 spk_do_flush();
2185 if (spk_special_handler) {
c6e3fd22
WH
2186 if (type == KT_SPEC && value == 1) {
2187 value = '\n';
2188 type = KT_LATIN;
0dcb2124 2189 } else if (type == KT_LETTER) {
c6e3fd22 2190 type = KT_LATIN;
0dcb2124 2191 } else if (value == 0x7f) {
16d35515 2192 value = 8; /* make del = backspace */
0dcb2124 2193 }
ca2beaf8 2194 ret = (*spk_special_handler) (vc, type, value, keycode);
c6e3fd22
WH
2195 spk_close_press = 0;
2196 if (ret < 0)
2197 bleep(9);
2198 goto out;
2199 }
2200 last_keycode = 0;
2201out:
3efe810f 2202 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
c6e3fd22
WH
2203 return ret;
2204}
2205
2206static int keyboard_notifier_call(struct notifier_block *nb,
16d35515 2207 unsigned long code, void *_param)
c6e3fd22
WH
2208{
2209 struct keyboard_notifier_param *param = _param;
2210 struct vc_data *vc = param->vc;
2211 int up = !param->down;
2212 int ret = NOTIFY_OK;
16d35515 2213 static int keycode; /* to hold the current keycode */
c6e3fd22
WH
2214
2215 if (vc->vc_mode == KD_GRAPHICS)
2216 return ret;
2217
2218 /*
2219 * First, determine whether we are handling a fake keypress on
2220 * the current processor. If we are, then return NOTIFY_OK,
2221 * to pass the keystroke up the chain. This prevents us from
2222 * trying to take the Speakup lock while it is held by the
2223 * processor on which the simulated keystroke was generated.
2224 * Also, the simulated keystrokes should be ignored by Speakup.
2225 */
2226
2227 if (speakup_fake_key_pressed())
2228 return ret;
2229
2230 switch (code) {
2231 case KBD_KEYCODE:
2232 /* speakup requires keycode and keysym currently */
2233 keycode = param->value;
2234 break;
2235 case KBD_UNBOUND_KEYCODE:
2236 /* not used yet */
2237 break;
2238 case KBD_UNICODE:
2239 /* not used yet */
2240 break;
2241 case KBD_KEYSYM:
2242 if (speakup_key(vc, param->shift, keycode, param->value, up))
2243 ret = NOTIFY_STOP;
16d35515
WH
2244 else if (KTYP(param->value) == KT_CUR)
2245 ret = pre_handle_cursor(vc, KVAL(param->value), up);
c6e3fd22 2246 break;
16d35515
WH
2247 case KBD_POST_KEYSYM:{
2248 unsigned char type = KTYP(param->value) - 0xf0;
2249 unsigned char val = KVAL(param->value);
8e69a811 2250
16d35515
WH
2251 switch (type) {
2252 case KT_SHIFT:
2253 do_handle_shift(vc, val, up);
2254 break;
2255 case KT_LATIN:
2256 case KT_LETTER:
2257 do_handle_latin(vc, val, up);
2258 break;
2259 case KT_CUR:
2260 do_handle_cursor(vc, val, up);
2261 break;
2262 case KT_SPEC:
2263 do_handle_spec(vc, val, up);
2264 break;
2265 }
c6e3fd22
WH
2266 break;
2267 }
c6e3fd22
WH
2268 }
2269 return ret;
2270}
2271
2272static int vt_notifier_call(struct notifier_block *nb,
16d35515 2273 unsigned long code, void *_param)
c6e3fd22
WH
2274{
2275 struct vt_notifier_param *param = _param;
2276 struct vc_data *vc = param->vc;
8e69a811 2277
c6e3fd22
WH
2278 switch (code) {
2279 case VT_ALLOCATE:
2280 if (vc->vc_mode == KD_TEXT)
b1b2b3ca 2281 speakup_allocate(vc, GFP_ATOMIC);
c6e3fd22
WH
2282 break;
2283 case VT_DEALLOCATE:
2284 speakup_deallocate(vc);
2285 break;
2286 case VT_WRITE:
0dcb2124 2287 if (param->c == '\b') {
c6e3fd22 2288 speakup_bs(vc);
0dcb2124 2289 } else {
9831013c 2290 u16 d = param->c;
650b175d 2291
16d35515
WH
2292 speakup_con_write(vc, &d, 1);
2293 }
c6e3fd22
WH
2294 break;
2295 case VT_UPDATE:
2296 speakup_con_update(vc);
2297 break;
2298 }
2299 return NOTIFY_OK;
2300}
2301
2302/* called by: module_exit() */
2303static void __exit speakup_exit(void)
2304{
2305 int i;
2306
c6e3fd22
WH
2307 unregister_keyboard_notifier(&keyboard_notifier_block);
2308 unregister_vt_notifier(&vt_notifier_block);
2309 speakup_unregister_devsynth();
d7500135 2310 speakup_cancel_paste();
c943e13b 2311 del_timer_sync(&cursor_timer);
c6e3fd22
WH
2312 kthread_stop(speakup_task);
2313 speakup_task = NULL;
2314 mutex_lock(&spk_mutex);
2315 synth_release();
2316 mutex_unlock(&spk_mutex);
e23a9b43 2317 spk_ttyio_unregister_ldisc();
c6e3fd22 2318
628f3428
CB
2319 speakup_kobj_exit();
2320
2321 for (i = 0; i < MAX_NR_CONSOLES; i++)
2322 kfree(speakup_console[i]);
2323
2324 speakup_remove_virtual_keyboard();
2325
c6e3fd22
WH
2326 for (i = 0; i < MAXVARS; i++)
2327 speakup_unregister_var(i);
2328
2329 for (i = 0; i < 256; i++) {
ca2beaf8
ST
2330 if (spk_characters[i] != spk_default_chars[i])
2331 kfree(spk_characters[i]);
c6e3fd22 2332 }
628f3428 2333
ca2beaf8 2334 spk_free_user_msgs();
c6e3fd22
WH
2335}
2336
2337/* call by: module_init() */
2338static int __init speakup_init(void)
2339{
2340 int i;
628f3428 2341 long err = 0;
c6e3fd22
WH
2342 struct vc_data *vc = vc_cons[fg_console].d;
2343 struct var_t *var;
2344
628f3428 2345 /* These first few initializations cannot fail. */
ca2beaf8
ST
2346 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2347 spk_reset_default_chars();
2348 spk_reset_default_chartab();
2349 spk_strlwr(synth_name);
c6e3fd22 2350 spk_vars[0].u.n.high = vc->vc_cols;
16d35515 2351 for (var = spk_vars; var->var_id != MAXVARS; var++)
c6e3fd22 2352 speakup_register_var(var);
16d35515
WH
2353 for (var = synth_time_vars;
2354 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
c6e3fd22 2355 speakup_register_var(var);
ca2beaf8 2356 for (i = 1; spk_punc_info[i].mask != 0; i++)
ff471ea8 2357 spk_set_mask_bits(NULL, i, 2);
c6e3fd22 2358
ca2beaf8 2359 spk_set_key_info(spk_key_defaults, spk_key_buf);
c6e3fd22 2360
628f3428
CB
2361 /* From here on out, initializations can fail. */
2362 err = speakup_add_virtual_keyboard();
2363 if (err)
2364 goto error_virtkeyboard;
2365
c6e3fd22 2366 for (i = 0; i < MAX_NR_CONSOLES; i++)
628f3428 2367 if (vc_cons[i].d) {
b1b2b3ca 2368 err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
628f3428
CB
2369 if (err)
2370 goto error_kobjects;
2371 }
2372
ca2beaf8 2373 if (spk_quiet_boot)
4afaee15
CB
2374 spk_shut_up |= 0x01;
2375
628f3428
CB
2376 err = speakup_kobj_init();
2377 if (err)
2378 goto error_kobjects;
c6e3fd22 2379
e23a9b43 2380 spk_ttyio_register_ldisc();
c6e3fd22
WH
2381 synth_init(synth_name);
2382 speakup_register_devsynth();
628f3428
CB
2383 /*
2384 * register_devsynth might fail, but this error is not fatal.
2385 * /dev/synth is an extra feature; the rest of Speakup
2386 * will work fine without it.
2387 */
c6e3fd22 2388
628f3428
CB
2389 err = register_keyboard_notifier(&keyboard_notifier_block);
2390 if (err)
2391 goto error_kbdnotifier;
2392 err = register_vt_notifier(&vt_notifier_block);
2393 if (err)
2394 goto error_vtnotifier;
c6e3fd22
WH
2395
2396 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
628f3428 2397
7959d556 2398 if (IS_ERR(speakup_task)) {
628f3428
CB
2399 err = PTR_ERR(speakup_task);
2400 goto error_task;
7959d556 2401 }
628f3428
CB
2402
2403 set_user_nice(speakup_task, 10);
7959d556 2404 wake_up_process(speakup_task);
628f3428
CB
2405
2406 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2407 pr_info("synth name on entry is: %s\n", synth_name);
7959d556
WH
2408 goto out;
2409
628f3428
CB
2410error_task:
2411 unregister_vt_notifier(&vt_notifier_block);
2412
2413error_vtnotifier:
2414 unregister_keyboard_notifier(&keyboard_notifier_block);
2415 del_timer(&cursor_timer);
2416
2417error_kbdnotifier:
2418 speakup_unregister_devsynth();
2419 mutex_lock(&spk_mutex);
2420 synth_release();
2421 mutex_unlock(&spk_mutex);
2422 speakup_kobj_exit();
2423
2424error_kobjects:
2425 for (i = 0; i < MAX_NR_CONSOLES; i++)
2426 kfree(speakup_console[i]);
2427
7959d556 2428 speakup_remove_virtual_keyboard();
628f3428
CB
2429
2430error_virtkeyboard:
2431 for (i = 0; i < MAXVARS; i++)
2432 speakup_unregister_var(i);
2433
2434 for (i = 0; i < 256; i++) {
ca2beaf8
ST
2435 if (spk_characters[i] != spk_default_chars[i])
2436 kfree(spk_characters[i]);
628f3428
CB
2437 }
2438
ca2beaf8 2439 spk_free_user_msgs();
628f3428 2440
7959d556
WH
2441out:
2442 return err;
c6e3fd22
WH
2443}
2444
c6e3fd22
WH
2445module_init(speakup_init);
2446module_exit(speakup_exit);