WIP FPC-III support
[linux/fpc-iii.git] / drivers / accessibility / speakup / main.c
blob428fceaf9d50e9386b8c8ea58920dad0868d055d
1 // SPDX-License-Identifier: GPL-2.0+
2 /* speakup.c
3 * review functions for the speakup screen review package.
4 * originally written by: Kirk Reiser and Andy Berdan.
6 * extensively modified by David Borowski.
8 ** Copyright (C) 1998 Kirk Reiser.
9 * Copyright (C) 2003 David Borowski.
12 #include <linux/kernel.h>
13 #include <linux/vt.h>
14 #include <linux/tty.h>
15 #include <linux/mm.h> /* __get_free_page() and friends */
16 #include <linux/vt_kern.h>
17 #include <linux/ctype.h>
18 #include <linux/selection.h>
19 #include <linux/unistd.h>
20 #include <linux/jiffies.h>
21 #include <linux/kthread.h>
22 #include <linux/keyboard.h> /* for KT_SHIFT */
23 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
24 #include <linux/input.h>
25 #include <linux/kmod.h>
27 /* speakup_*_selection */
28 #include <linux/module.h>
29 #include <linux/sched.h>
30 #include <linux/slab.h>
31 #include <linux/types.h>
32 #include <linux/consolemap.h>
34 #include <linux/spinlock.h>
35 #include <linux/notifier.h>
37 #include <linux/uaccess.h> /* copy_from|to|user() and others */
39 #include "spk_priv.h"
40 #include "speakup.h"
42 #define MAX_DELAY msecs_to_jiffies(500)
43 #define MINECHOCHAR SPACE
45 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
46 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
47 MODULE_DESCRIPTION("Speakup console speech");
48 MODULE_LICENSE("GPL");
49 MODULE_VERSION(SPEAKUP_VERSION);
51 char *synth_name;
52 module_param_named(synth, synth_name, charp, 0444);
53 module_param_named(quiet, spk_quiet_boot, bool, 0444);
55 MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
56 MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
58 special_func spk_special_handler;
60 short spk_pitch_shift, synth_flags;
61 static u16 buf[256];
62 int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
63 int spk_no_intr, spk_spell_delay;
64 int spk_key_echo, spk_say_word_ctl;
65 int spk_say_ctrl, spk_bell_pos;
66 short spk_punc_mask;
67 int spk_punc_level, spk_reading_punc;
68 char spk_str_caps_start[MAXVARLEN + 1] = "\0";
69 char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
70 char spk_str_pause[MAXVARLEN + 1] = "\0";
71 bool spk_paused;
72 const struct st_bits_data spk_punc_info[] = {
73 {"none", "", 0},
74 {"some", "/$%&@", SOME},
75 {"most", "$%&#()=+*/@^<>|\\", MOST},
76 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
77 {"delimiters", "", B_WDLM},
78 {"repeats", "()", CH_RPT},
79 {"extended numeric", "", B_EXNUM},
80 {"symbols", "", B_SYM},
81 {NULL, NULL}
84 static char mark_cut_flag;
85 #define MAX_KEY 160
86 static u_char *spk_shift_table;
87 u_char *spk_our_keys[MAX_KEY];
88 u_char spk_key_buf[600];
89 const u_char spk_key_defaults[] = {
90 #include "speakupmap.h"
93 /* cursor track modes, must be ordered same as cursor_msgs in enum msg_index_t */
94 enum cursor_track {
95 CT_Off = 0,
96 CT_On,
97 CT_Highlight,
98 CT_Window,
99 CT_Max,
100 read_all_mode = CT_Max,
103 /* Speakup Cursor Track Variables */
104 static enum cursor_track cursor_track = 1, prev_cursor_track = 1;
106 static struct tty_struct *tty;
108 static void spkup_write(const u16 *in_buf, int count);
110 static char *phonetic[] = {
111 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
112 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
113 "papa",
114 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
115 "x ray", "yankee", "zulu"
118 /* array of 256 char pointers (one for each character description)
119 * initialized to default_chars and user selectable via
120 * /proc/speakup/characters
122 char *spk_characters[256];
124 char *spk_default_chars[256] = {
125 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
126 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
127 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
128 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
129 "control",
130 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
131 "tick",
132 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
133 "dot",
134 "slash",
135 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
136 "eight", "nine",
137 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
138 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
139 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
140 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
141 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
142 "caret",
143 "line",
144 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
145 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
146 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
147 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
148 /*127*/ "del", "control", "control", "control", "control", "control",
149 "control", "control", "control", "control", "control",
150 /*138*/ "control", "control", "control", "control", "control",
151 "control", "control", "control", "control", "control",
152 "control", "control",
153 /*150*/ "control", "control", "control", "control", "control",
154 "control", "control", "control", "control", "control",
155 /*160*/ "nbsp", "inverted bang",
156 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
157 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
158 /*172*/ "not", "soft hyphen", "registered", "macron",
159 /*176*/ "degrees", "plus or minus", "super two", "super three",
160 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
161 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
162 /*188*/ "one quarter", "one half", "three quarters",
163 "inverted question",
164 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
165 "A RING",
166 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
167 "E OOMLAUT",
168 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
169 "N TILDE",
170 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
171 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
172 "U CIRCUMFLEX",
173 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
174 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
175 /*230*/ "ae", "c cidella", "e grave", "e acute",
176 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
177 "i circumflex",
178 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
179 "o circumflex",
180 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
181 "u acute",
182 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
185 /* array of 256 u_short (one for each character)
186 * initialized to default_chartab and user selectable via
187 * /sys/module/speakup/parameters/chartab
189 u_short spk_chartab[256];
191 static u_short default_chartab[256] = {
192 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
193 B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
194 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
195 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */
196 WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */
197 PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */
198 NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */
199 NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */
200 PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */
201 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */
202 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */
203 A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */
204 PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */
205 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */
206 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */
207 ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */
208 B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
209 B_SYM, /* 135 */
210 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
211 B_CAPSYM, /* 143 */
212 B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
213 B_SYM, /* 151 */
214 B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
215 B_SYM, /* 159 */
216 WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
217 B_SYM, /* 167 */
218 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */
219 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */
220 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */
221 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */
222 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */
223 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */
224 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */
225 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */
226 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */
227 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */
228 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */
231 struct task_struct *speakup_task;
232 struct bleep spk_unprocessed_sound;
233 static int spk_keydown;
234 static u16 spk_lastkey;
235 static u_char spk_close_press, keymap_flags;
236 static u_char last_keycode, this_speakup_key;
237 static u_long last_spk_jiffy;
239 struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
241 DEFINE_MUTEX(spk_mutex);
243 static int keyboard_notifier_call(struct notifier_block *,
244 unsigned long code, void *param);
246 static struct notifier_block keyboard_notifier_block = {
247 .notifier_call = keyboard_notifier_call,
250 static int vt_notifier_call(struct notifier_block *,
251 unsigned long code, void *param);
253 static struct notifier_block vt_notifier_block = {
254 .notifier_call = vt_notifier_call,
257 static unsigned char get_attributes(struct vc_data *vc, u16 *pos)
259 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
260 return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;
263 static void speakup_date(struct vc_data *vc)
265 spk_x = spk_cx = vc->state.x;
266 spk_y = spk_cy = vc->state.y;
267 spk_pos = spk_cp = vc->vc_pos;
268 spk_old_attr = spk_attr;
269 spk_attr = get_attributes(vc, (u_short *)spk_pos);
272 static void bleep(u_short val)
274 static const short vals[] = {
275 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
277 short freq;
278 int time = spk_bleep_time;
280 freq = vals[val % 12];
281 if (val > 11)
282 freq *= (1 << (val / 12));
283 spk_unprocessed_sound.freq = freq;
284 spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
285 spk_unprocessed_sound.active = 1;
286 /* We can only have 1 active sound at a time. */
289 static void speakup_shut_up(struct vc_data *vc)
291 if (spk_killed)
292 return;
293 spk_shut_up |= 0x01;
294 spk_parked &= 0xfe;
295 speakup_date(vc);
296 if (synth)
297 spk_do_flush();
300 static void speech_kill(struct vc_data *vc)
302 char val = synth->is_alive(synth);
304 if (val == 0)
305 return;
307 /* re-enables synth, if disabled */
308 if (val == 2 || spk_killed) {
309 /* dead */
310 spk_shut_up &= ~0x40;
311 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
312 } else {
313 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
314 spk_shut_up |= 0x40;
318 static void speakup_off(struct vc_data *vc)
320 if (spk_shut_up & 0x80) {
321 spk_shut_up &= 0x7f;
322 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
323 } else {
324 spk_shut_up |= 0x80;
325 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
327 speakup_date(vc);
330 static void speakup_parked(struct vc_data *vc)
332 if (spk_parked & 0x80) {
333 spk_parked = 0;
334 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
335 } else {
336 spk_parked |= 0x80;
337 synth_printf("%s\n", spk_msg_get(MSG_PARKED));
341 static void speakup_cut(struct vc_data *vc)
343 static const char err_buf[] = "set selection failed";
344 int ret;
346 if (!mark_cut_flag) {
347 mark_cut_flag = 1;
348 spk_xs = (u_short)spk_x;
349 spk_ys = (u_short)spk_y;
350 spk_sel_cons = vc;
351 synth_printf("%s\n", spk_msg_get(MSG_MARK));
352 return;
354 spk_xe = (u_short)spk_x;
355 spk_ye = (u_short)spk_y;
356 mark_cut_flag = 0;
357 synth_printf("%s\n", spk_msg_get(MSG_CUT));
359 ret = speakup_set_selection(tty);
361 switch (ret) {
362 case 0:
363 break; /* no error */
364 case -EFAULT:
365 pr_warn("%sEFAULT\n", err_buf);
366 break;
367 case -EINVAL:
368 pr_warn("%sEINVAL\n", err_buf);
369 break;
370 case -ENOMEM:
371 pr_warn("%sENOMEM\n", err_buf);
372 break;
376 static void speakup_paste(struct vc_data *vc)
378 if (mark_cut_flag) {
379 mark_cut_flag = 0;
380 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
381 } else {
382 synth_printf("%s\n", spk_msg_get(MSG_PASTE));
383 speakup_paste_selection(tty);
387 static void say_attributes(struct vc_data *vc)
389 int fg = spk_attr & 0x0f;
390 int bg = spk_attr >> 4;
392 if (fg > 8) {
393 synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
394 fg -= 8;
396 synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
397 if (bg > 7) {
398 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
399 bg -= 8;
400 } else {
401 synth_printf(" %s ", spk_msg_get(MSG_ON));
403 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
406 /* must be ordered same as edge_msgs in enum msg_index_t */
407 enum edge {
408 edge_none = 0,
409 edge_top,
410 edge_bottom,
411 edge_left,
412 edge_right,
413 edge_quiet
416 static void announce_edge(struct vc_data *vc, enum edge msg_id)
418 if (spk_bleeps & 1)
419 bleep(spk_y);
420 if ((spk_bleeps & 2) && (msg_id < edge_quiet))
421 synth_printf("%s\n",
422 spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
425 static void speak_char(u16 ch)
427 char *cp;
428 struct var_t *direct = spk_get_var(DIRECT);
430 if (ch >= 0x100 || (direct && direct->u.n.value)) {
431 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
432 spk_pitch_shift++;
433 synth_printf("%s", spk_str_caps_start);
435 synth_putwc_s(ch);
436 if (ch < 0x100 && IS_CHAR(ch, B_CAP))
437 synth_printf("%s", spk_str_caps_stop);
438 return;
441 cp = spk_characters[ch];
442 if (!cp) {
443 pr_info("%s: cp == NULL!\n", __func__);
444 return;
446 if (IS_CHAR(ch, B_CAP)) {
447 spk_pitch_shift++;
448 synth_printf("%s %s %s",
449 spk_str_caps_start, cp, spk_str_caps_stop);
450 } else {
451 if (*cp == '^') {
452 cp++;
453 synth_printf(" %s%s ", spk_msg_get(MSG_CTRL), cp);
454 } else {
455 synth_printf(" %s ", cp);
460 static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
462 u16 ch = ' ';
464 if (vc && pos) {
465 u16 w;
466 u16 c;
468 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, true);
469 w = scr_readw(pos);
470 c = w & 0xff;
472 if (w & vc->vc_hi_font_mask) {
473 w &= ~vc->vc_hi_font_mask;
474 c |= 0x100;
477 ch = inverse_translate(vc, c, 1);
478 *attribs = (w & 0xff00) >> 8;
480 return ch;
483 static void say_char(struct vc_data *vc)
485 u16 ch;
487 spk_old_attr = spk_attr;
488 ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
489 if (spk_attr != spk_old_attr) {
490 if (spk_attrib_bleep & 1)
491 bleep(spk_y);
492 if (spk_attrib_bleep & 2)
493 say_attributes(vc);
495 speak_char(ch);
498 static void say_phonetic_char(struct vc_data *vc)
500 u16 ch;
502 spk_old_attr = spk_attr;
503 ch = get_char(vc, (u_short *)spk_pos, &spk_attr);
504 if (ch <= 0x7f && isalpha(ch)) {
505 ch &= 0x1f;
506 synth_printf("%s\n", phonetic[--ch]);
507 } else {
508 if (ch < 0x100 && IS_CHAR(ch, B_NUM))
509 synth_printf("%s ", spk_msg_get(MSG_NUMBER));
510 speak_char(ch);
514 static void say_prev_char(struct vc_data *vc)
516 spk_parked |= 0x01;
517 if (spk_x == 0) {
518 announce_edge(vc, edge_left);
519 return;
521 spk_x--;
522 spk_pos -= 2;
523 say_char(vc);
526 static void say_next_char(struct vc_data *vc)
528 spk_parked |= 0x01;
529 if (spk_x == vc->vc_cols - 1) {
530 announce_edge(vc, edge_right);
531 return;
533 spk_x++;
534 spk_pos += 2;
535 say_char(vc);
538 /* get_word - will first check to see if the character under the
539 * reading cursor is a space and if spk_say_word_ctl is true it will
540 * return the word space. If spk_say_word_ctl is not set it will check to
541 * see if there is a word starting on the next position to the right
542 * and return that word if it exists. If it does not exist it will
543 * move left to the beginning of any previous word on the line or the
544 * beginning off the line whichever comes first..
547 static u_long get_word(struct vc_data *vc)
549 u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
550 u16 ch;
551 u16 attr_ch;
552 u_char temp;
554 spk_old_attr = spk_attr;
555 ch = get_char(vc, (u_short *)tmp_pos, &temp);
557 /* decided to take out the sayword if on a space (mis-information */
558 if (spk_say_word_ctl && ch == SPACE) {
559 *buf = '\0';
560 synth_printf("%s\n", spk_msg_get(MSG_SPACE));
561 return 0;
562 } else if (tmpx < vc->vc_cols - 2 &&
563 (ch == SPACE || ch == 0 || (ch < 0x100 && IS_WDLM(ch))) &&
564 get_char(vc, (u_short *)tmp_pos + 1, &temp) > SPACE) {
565 tmp_pos += 2;
566 tmpx++;
567 } else {
568 while (tmpx > 0) {
569 ch = get_char(vc, (u_short *)tmp_pos - 1, &temp);
570 if ((ch == SPACE || ch == 0 ||
571 (ch < 0x100 && IS_WDLM(ch))) &&
572 get_char(vc, (u_short *)tmp_pos, &temp) > SPACE)
573 break;
574 tmp_pos -= 2;
575 tmpx--;
578 attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr);
579 buf[cnt++] = attr_ch;
580 while (tmpx < vc->vc_cols - 1) {
581 tmp_pos += 2;
582 tmpx++;
583 ch = get_char(vc, (u_short *)tmp_pos, &temp);
584 if (ch == SPACE || ch == 0 ||
585 (buf[cnt - 1] < 0x100 && IS_WDLM(buf[cnt - 1]) &&
586 ch > SPACE))
587 break;
588 buf[cnt++] = ch;
590 buf[cnt] = '\0';
591 return cnt;
594 static void say_word(struct vc_data *vc)
596 u_long cnt = get_word(vc);
597 u_short saved_punc_mask = spk_punc_mask;
599 if (cnt == 0)
600 return;
601 spk_punc_mask = PUNC;
602 buf[cnt++] = SPACE;
603 spkup_write(buf, cnt);
604 spk_punc_mask = saved_punc_mask;
607 static void say_prev_word(struct vc_data *vc)
609 u_char temp;
610 u16 ch;
611 enum edge edge_said = edge_none;
612 u_short last_state = 0, state = 0;
614 spk_parked |= 0x01;
616 if (spk_x == 0) {
617 if (spk_y == 0) {
618 announce_edge(vc, edge_top);
619 return;
621 spk_y--;
622 spk_x = vc->vc_cols;
623 edge_said = edge_quiet;
625 while (1) {
626 if (spk_x == 0) {
627 if (spk_y == 0) {
628 edge_said = edge_top;
629 break;
631 if (edge_said != edge_quiet)
632 edge_said = edge_left;
633 if (state > 0)
634 break;
635 spk_y--;
636 spk_x = vc->vc_cols - 1;
637 } else {
638 spk_x--;
640 spk_pos -= 2;
641 ch = get_char(vc, (u_short *)spk_pos, &temp);
642 if (ch == SPACE || ch == 0)
643 state = 0;
644 else if (ch < 0x100 && IS_WDLM(ch))
645 state = 1;
646 else
647 state = 2;
648 if (state < last_state) {
649 spk_pos += 2;
650 spk_x++;
651 break;
653 last_state = state;
655 if (spk_x == 0 && edge_said == edge_quiet)
656 edge_said = edge_left;
657 if (edge_said > edge_none && edge_said < edge_quiet)
658 announce_edge(vc, edge_said);
659 say_word(vc);
662 static void say_next_word(struct vc_data *vc)
664 u_char temp;
665 u16 ch;
666 enum edge edge_said = edge_none;
667 u_short last_state = 2, state = 0;
669 spk_parked |= 0x01;
670 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
671 announce_edge(vc, edge_bottom);
672 return;
674 while (1) {
675 ch = get_char(vc, (u_short *)spk_pos, &temp);
676 if (ch == SPACE || ch == 0)
677 state = 0;
678 else if (ch < 0x100 && IS_WDLM(ch))
679 state = 1;
680 else
681 state = 2;
682 if (state > last_state)
683 break;
684 if (spk_x >= vc->vc_cols - 1) {
685 if (spk_y == vc->vc_rows - 1) {
686 edge_said = edge_bottom;
687 break;
689 state = 0;
690 spk_y++;
691 spk_x = 0;
692 edge_said = edge_right;
693 } else {
694 spk_x++;
696 spk_pos += 2;
697 last_state = state;
699 if (edge_said > edge_none)
700 announce_edge(vc, edge_said);
701 say_word(vc);
704 static void spell_word(struct vc_data *vc)
706 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
707 u16 *cp = buf;
708 char *cp1;
709 char *str_cap = spk_str_caps_stop;
710 char *last_cap = spk_str_caps_stop;
711 struct var_t *direct = spk_get_var(DIRECT);
712 u16 ch;
714 if (!get_word(vc))
715 return;
716 while ((ch = *cp)) {
717 if (cp != buf)
718 synth_printf(" %s ", delay_str[spk_spell_delay]);
719 /* FIXME: Non-latin1 considered as lower case */
720 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
721 str_cap = spk_str_caps_start;
722 if (*spk_str_caps_stop)
723 spk_pitch_shift++;
724 else /* synth has no pitch */
725 last_cap = spk_str_caps_stop;
726 } else {
727 str_cap = spk_str_caps_stop;
729 if (str_cap != last_cap) {
730 synth_printf("%s", str_cap);
731 last_cap = str_cap;
733 if (ch >= 0x100 || (direct && direct->u.n.value)) {
734 synth_putwc_s(ch);
735 } else if (this_speakup_key == SPELL_PHONETIC &&
736 ch <= 0x7f && isalpha(ch)) {
737 ch &= 0x1f;
738 cp1 = phonetic[--ch];
739 synth_printf("%s", cp1);
740 } else {
741 cp1 = spk_characters[ch];
742 if (*cp1 == '^') {
743 synth_printf("%s", spk_msg_get(MSG_CTRL));
744 cp1++;
746 synth_printf("%s", cp1);
748 cp++;
750 if (str_cap != spk_str_caps_stop)
751 synth_printf("%s", spk_str_caps_stop);
754 static int get_line(struct vc_data *vc)
756 u_long tmp = spk_pos - (spk_x * 2);
757 int i = 0;
758 u_char tmp2;
760 spk_old_attr = spk_attr;
761 spk_attr = get_attributes(vc, (u_short *)spk_pos);
762 for (i = 0; i < vc->vc_cols; i++) {
763 buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
764 tmp += 2;
766 for (--i; i >= 0; i--)
767 if (buf[i] != SPACE)
768 break;
769 return ++i;
772 static void say_line(struct vc_data *vc)
774 int i = get_line(vc);
775 u16 *cp;
776 u_short saved_punc_mask = spk_punc_mask;
778 if (i == 0) {
779 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
780 return;
782 buf[i++] = '\n';
783 if (this_speakup_key == SAY_LINE_INDENT) {
784 cp = buf;
785 while (*cp == SPACE)
786 cp++;
787 synth_printf("%zd, ", (cp - buf) + 1);
789 spk_punc_mask = spk_punc_masks[spk_reading_punc];
790 spkup_write(buf, i);
791 spk_punc_mask = saved_punc_mask;
794 static void say_prev_line(struct vc_data *vc)
796 spk_parked |= 0x01;
797 if (spk_y == 0) {
798 announce_edge(vc, edge_top);
799 return;
801 spk_y--;
802 spk_pos -= vc->vc_size_row;
803 say_line(vc);
806 static void say_next_line(struct vc_data *vc)
808 spk_parked |= 0x01;
809 if (spk_y == vc->vc_rows - 1) {
810 announce_edge(vc, edge_bottom);
811 return;
813 spk_y++;
814 spk_pos += vc->vc_size_row;
815 say_line(vc);
818 static int say_from_to(struct vc_data *vc, u_long from, u_long to,
819 int read_punc)
821 int i = 0;
822 u_char tmp;
823 u_short saved_punc_mask = spk_punc_mask;
825 spk_old_attr = spk_attr;
826 spk_attr = get_attributes(vc, (u_short *)from);
827 while (from < to) {
828 buf[i++] = get_char(vc, (u_short *)from, &tmp);
829 from += 2;
830 if (i >= vc->vc_size_row)
831 break;
833 for (--i; i >= 0; i--)
834 if (buf[i] != SPACE)
835 break;
836 buf[++i] = SPACE;
837 buf[++i] = '\0';
838 if (i < 1)
839 return i;
840 if (read_punc)
841 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
842 spkup_write(buf, i);
843 if (read_punc)
844 spk_punc_mask = saved_punc_mask;
845 return i - 1;
848 static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
849 int read_punc)
851 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
852 u_long end = start + (to * 2);
854 start += from * 2;
855 if (say_from_to(vc, start, end, read_punc) <= 0)
856 if (cursor_track != read_all_mode)
857 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
860 /* Sentence Reading Commands */
862 static int currsentence;
863 static int numsentences[2];
864 static u16 *sentbufend[2];
865 static u16 *sentmarks[2][10];
866 static int currbuf;
867 static int bn;
868 static u16 sentbuf[2][256];
870 static int say_sentence_num(int num, int prev)
872 bn = currbuf;
873 currsentence = num + 1;
874 if (prev && --bn == -1)
875 bn = 1;
877 if (num > numsentences[bn])
878 return 0;
880 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
881 return 1;
884 static int get_sentence_buf(struct vc_data *vc, int read_punc)
886 u_long start, end;
887 int i, bn;
888 u_char tmp;
890 currbuf++;
891 if (currbuf == 2)
892 currbuf = 0;
893 bn = currbuf;
894 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
895 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
897 numsentences[bn] = 0;
898 sentmarks[bn][0] = &sentbuf[bn][0];
899 i = 0;
900 spk_old_attr = spk_attr;
901 spk_attr = get_attributes(vc, (u_short *)start);
903 while (start < end) {
904 sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
905 if (i > 0) {
906 if (sentbuf[bn][i] == SPACE &&
907 sentbuf[bn][i - 1] == '.' &&
908 numsentences[bn] < 9) {
909 /* Sentence Marker */
910 numsentences[bn]++;
911 sentmarks[bn][numsentences[bn]] =
912 &sentbuf[bn][i];
915 i++;
916 start += 2;
917 if (i >= vc->vc_size_row)
918 break;
921 for (--i; i >= 0; i--)
922 if (sentbuf[bn][i] != SPACE)
923 break;
925 if (i < 1)
926 return -1;
928 sentbuf[bn][++i] = SPACE;
929 sentbuf[bn][++i] = '\0';
931 sentbufend[bn] = &sentbuf[bn][i];
932 return numsentences[bn];
935 static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
937 u_long start = vc->vc_origin, end;
939 if (from > 0)
940 start += from * vc->vc_size_row;
941 if (to > vc->vc_rows)
942 to = vc->vc_rows;
943 end = vc->vc_origin + (to * vc->vc_size_row);
944 for (from = start; from < end; from = to) {
945 to = from + vc->vc_size_row;
946 say_from_to(vc, from, to, 1);
950 static void say_screen(struct vc_data *vc)
952 say_screen_from_to(vc, 0, vc->vc_rows);
955 static void speakup_win_say(struct vc_data *vc)
957 u_long start, end, from, to;
959 if (win_start < 2) {
960 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
961 return;
963 start = vc->vc_origin + (win_top * vc->vc_size_row);
964 end = vc->vc_origin + (win_bottom * vc->vc_size_row);
965 while (start <= end) {
966 from = start + (win_left * 2);
967 to = start + (win_right * 2);
968 say_from_to(vc, from, to, 1);
969 start += vc->vc_size_row;
973 static void top_edge(struct vc_data *vc)
975 spk_parked |= 0x01;
976 spk_pos = vc->vc_origin + 2 * spk_x;
977 spk_y = 0;
978 say_line(vc);
981 static void bottom_edge(struct vc_data *vc)
983 spk_parked |= 0x01;
984 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
985 spk_y = vc->vc_rows - 1;
986 say_line(vc);
989 static void left_edge(struct vc_data *vc)
991 spk_parked |= 0x01;
992 spk_pos -= spk_x * 2;
993 spk_x = 0;
994 say_char(vc);
997 static void right_edge(struct vc_data *vc)
999 spk_parked |= 0x01;
1000 spk_pos += (vc->vc_cols - spk_x - 1) * 2;
1001 spk_x = vc->vc_cols - 1;
1002 say_char(vc);
1005 static void say_first_char(struct vc_data *vc)
1007 int i, len = get_line(vc);
1008 u16 ch;
1010 spk_parked |= 0x01;
1011 if (len == 0) {
1012 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1013 return;
1015 for (i = 0; i < len; i++)
1016 if (buf[i] != SPACE)
1017 break;
1018 ch = buf[i];
1019 spk_pos -= (spk_x - i) * 2;
1020 spk_x = i;
1021 synth_printf("%d, ", ++i);
1022 speak_char(ch);
1025 static void say_last_char(struct vc_data *vc)
1027 int len = get_line(vc);
1028 u16 ch;
1030 spk_parked |= 0x01;
1031 if (len == 0) {
1032 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1033 return;
1035 ch = buf[--len];
1036 spk_pos -= (spk_x - len) * 2;
1037 spk_x = len;
1038 synth_printf("%d, ", ++len);
1039 speak_char(ch);
1042 static void say_position(struct vc_data *vc)
1044 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
1045 vc->vc_num + 1);
1046 synth_printf("\n");
1049 /* Added by brianb */
1050 static void say_char_num(struct vc_data *vc)
1052 u_char tmp;
1053 u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
1055 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
1058 /* these are stub functions to keep keyboard.c happy. */
1060 static void say_from_top(struct vc_data *vc)
1062 say_screen_from_to(vc, 0, spk_y);
1065 static void say_to_bottom(struct vc_data *vc)
1067 say_screen_from_to(vc, spk_y, vc->vc_rows);
1070 static void say_from_left(struct vc_data *vc)
1072 say_line_from_to(vc, 0, spk_x, 1);
1075 static void say_to_right(struct vc_data *vc)
1077 say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1080 /* end of stub functions. */
1082 static void spkup_write(const u16 *in_buf, int count)
1084 static int rep_count;
1085 static u16 ch = '\0', old_ch = '\0';
1086 static u_short char_type, last_type;
1087 int in_count = count;
1089 spk_keydown = 0;
1090 while (count--) {
1091 if (cursor_track == read_all_mode) {
1092 /* Insert Sentence Index */
1093 if ((in_buf == sentmarks[bn][currsentence]) &&
1094 (currsentence <= numsentences[bn]))
1095 synth_insert_next_index(currsentence++);
1097 ch = *in_buf++;
1098 if (ch < 0x100)
1099 char_type = spk_chartab[ch];
1100 else
1101 char_type = ALPHA;
1102 if (ch == old_ch && !(char_type & B_NUM)) {
1103 if (++rep_count > 2)
1104 continue;
1105 } else {
1106 if ((last_type & CH_RPT) && rep_count > 2) {
1107 synth_printf(" ");
1108 synth_printf(spk_msg_get(MSG_REPEAT_DESC),
1109 ++rep_count);
1110 synth_printf(" ");
1112 rep_count = 0;
1114 if (ch == spk_lastkey) {
1115 rep_count = 0;
1116 if (spk_key_echo == 1 && ch >= MINECHOCHAR)
1117 speak_char(ch);
1118 } else if (char_type & B_ALPHA) {
1119 if ((synth_flags & SF_DEC) && (last_type & PUNC))
1120 synth_buffer_add(SPACE);
1121 synth_putwc_s(ch);
1122 } else if (char_type & B_NUM) {
1123 rep_count = 0;
1124 synth_putwc_s(ch);
1125 } else if (char_type & spk_punc_mask) {
1126 speak_char(ch);
1127 char_type &= ~PUNC; /* for dec nospell processing */
1128 } else if (char_type & SYNTH_OK) {
1129 /* these are usually puncts like . and , which synth
1130 * needs for expression.
1131 * suppress multiple to get rid of long pauses and
1132 * clear repeat count
1133 * so if someone has
1134 * repeats on you don't get nothing repeated count
1136 if (ch != old_ch)
1137 synth_putwc_s(ch);
1138 else
1139 rep_count = 0;
1140 } else {
1141 /* send space and record position, if next is num overwrite space */
1142 if (old_ch != ch)
1143 synth_buffer_add(SPACE);
1144 else
1145 rep_count = 0;
1147 old_ch = ch;
1148 last_type = char_type;
1150 spk_lastkey = 0;
1151 if (in_count > 2 && rep_count > 2) {
1152 if (last_type & CH_RPT) {
1153 synth_printf(" ");
1154 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1155 ++rep_count);
1156 synth_printf(" ");
1158 rep_count = 0;
1162 static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1164 static void read_all_doc(struct vc_data *vc);
1165 static void cursor_done(struct timer_list *unused);
1166 static DEFINE_TIMER(cursor_timer, cursor_done);
1168 static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1170 unsigned long flags;
1172 if (!synth || up_flag || spk_killed)
1173 return;
1174 spin_lock_irqsave(&speakup_info.spinlock, flags);
1175 if (cursor_track == read_all_mode) {
1176 switch (value) {
1177 case KVAL(K_SHIFT):
1178 del_timer(&cursor_timer);
1179 spk_shut_up &= 0xfe;
1180 spk_do_flush();
1181 read_all_doc(vc);
1182 break;
1183 case KVAL(K_CTRL):
1184 del_timer(&cursor_timer);
1185 cursor_track = prev_cursor_track;
1186 spk_shut_up &= 0xfe;
1187 spk_do_flush();
1188 break;
1190 } else {
1191 spk_shut_up &= 0xfe;
1192 spk_do_flush();
1194 if (spk_say_ctrl && value < NUM_CTL_LABELS)
1195 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
1196 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1199 static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1201 unsigned long flags;
1203 spin_lock_irqsave(&speakup_info.spinlock, flags);
1204 if (up_flag) {
1205 spk_lastkey = 0;
1206 spk_keydown = 0;
1207 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1208 return;
1210 if (!synth || spk_killed) {
1211 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1212 return;
1214 spk_shut_up &= 0xfe;
1215 spk_lastkey = value;
1216 spk_keydown++;
1217 spk_parked &= 0xfe;
1218 if (spk_key_echo == 2 && value >= MINECHOCHAR)
1219 speak_char(value);
1220 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1223 int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
1225 int i = 0, states, key_data_len;
1226 const u_char *cp = key_info;
1227 u_char *cp1 = k_buffer;
1228 u_char ch, version, num_keys;
1230 version = *cp++;
1231 if (version != KEY_MAP_VER) {
1232 pr_debug("version found %d should be %d\n",
1233 version, KEY_MAP_VER);
1234 return -EINVAL;
1236 num_keys = *cp;
1237 states = (int)cp[1];
1238 key_data_len = (states + 1) * (num_keys + 1);
1239 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
1240 pr_debug("too many key_infos (%d over %u)\n",
1241 key_data_len + SHIFT_TBL_SIZE + 4,
1242 (unsigned int)(sizeof(spk_key_buf)));
1243 return -EINVAL;
1245 memset(k_buffer, 0, SHIFT_TBL_SIZE);
1246 memset(spk_our_keys, 0, sizeof(spk_our_keys));
1247 spk_shift_table = k_buffer;
1248 spk_our_keys[0] = spk_shift_table;
1249 cp1 += SHIFT_TBL_SIZE;
1250 memcpy(cp1, cp, key_data_len + 3);
1251 /* get num_keys, states and data */
1252 cp1 += 2; /* now pointing at shift states */
1253 for (i = 1; i <= states; i++) {
1254 ch = *cp1++;
1255 if (ch >= SHIFT_TBL_SIZE) {
1256 pr_debug("(%d) not valid shift state (max_allowed = %d)\n",
1257 ch, SHIFT_TBL_SIZE);
1258 return -EINVAL;
1260 spk_shift_table[ch] = i;
1262 keymap_flags = *cp1++;
1263 while ((ch = *cp1)) {
1264 if (ch >= MAX_KEY) {
1265 pr_debug("(%d), not valid key, (max_allowed = %d)\n",
1266 ch, MAX_KEY);
1267 return -EINVAL;
1269 spk_our_keys[ch] = cp1;
1270 cp1 += states + 1;
1272 return 0;
1275 static struct var_t spk_vars[] = {
1276 /* bell must be first to set high limit */
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} },
1289 V_LAST_VAR
1292 static void toggle_cursoring(struct vc_data *vc)
1294 if (cursor_track == read_all_mode)
1295 cursor_track = prev_cursor_track;
1296 if (++cursor_track >= CT_Max)
1297 cursor_track = 0;
1298 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
1301 void spk_reset_default_chars(void)
1303 int i;
1305 /* First, free any non-default */
1306 for (i = 0; i < 256; i++) {
1307 if (spk_characters[i] &&
1308 (spk_characters[i] != spk_default_chars[i]))
1309 kfree(spk_characters[i]);
1312 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
1315 void spk_reset_default_chartab(void)
1317 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1320 static const struct st_bits_data *pb_edit;
1322 static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1324 short mask = pb_edit->mask, ch_type = spk_chartab[ch];
1326 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
1327 return -1;
1328 if (ch == SPACE) {
1329 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1330 spk_special_handler = NULL;
1331 return 1;
1333 if (mask < PUNC && !(ch_type & PUNC))
1334 return -1;
1335 spk_chartab[ch] ^= mask;
1336 speak_char(ch);
1337 synth_printf(" %s\n",
1338 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1339 spk_msg_get(MSG_OFF));
1340 return 1;
1343 /* Allocation concurrency is protected by the console semaphore */
1344 static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)
1346 int vc_num;
1348 vc_num = vc->vc_num;
1349 if (!speakup_console[vc_num]) {
1350 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
1351 gfp_flags);
1352 if (!speakup_console[vc_num])
1353 return -ENOMEM;
1354 speakup_date(vc);
1355 } else if (!spk_parked) {
1356 speakup_date(vc);
1359 return 0;
1362 static void speakup_deallocate(struct vc_data *vc)
1364 int vc_num;
1366 vc_num = vc->vc_num;
1367 kfree(speakup_console[vc_num]);
1368 speakup_console[vc_num] = NULL;
1371 enum read_all_command {
1372 RA_NEXT_SENT = KVAL(K_DOWN)+1,
1373 RA_PREV_LINE = KVAL(K_LEFT)+1,
1374 RA_NEXT_LINE = KVAL(K_RIGHT)+1,
1375 RA_PREV_SENT = KVAL(K_UP)+1,
1376 RA_DOWN_ARROW,
1377 RA_TIMER,
1378 RA_FIND_NEXT_SENT,
1379 RA_FIND_PREV_SENT,
1382 static u_char is_cursor;
1383 static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1384 static int cursor_con;
1386 static void reset_highlight_buffers(struct vc_data *);
1388 static enum read_all_command read_all_key;
1390 static int in_keyboard_notifier;
1392 static void start_read_all_timer(struct vc_data *vc, enum read_all_command command);
1394 static void kbd_fakekey2(struct vc_data *vc, enum read_all_command command)
1396 del_timer(&cursor_timer);
1397 speakup_fake_down_arrow();
1398 start_read_all_timer(vc, command);
1401 static void read_all_doc(struct vc_data *vc)
1403 if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
1404 return;
1405 if (!synth_supports_indexing())
1406 return;
1407 if (cursor_track != read_all_mode)
1408 prev_cursor_track = cursor_track;
1409 cursor_track = read_all_mode;
1410 spk_reset_index_count(0);
1411 if (get_sentence_buf(vc, 0) == -1) {
1412 del_timer(&cursor_timer);
1413 if (!in_keyboard_notifier)
1414 speakup_fake_down_arrow();
1415 start_read_all_timer(vc, RA_DOWN_ARROW);
1416 } else {
1417 say_sentence_num(0, 0);
1418 synth_insert_next_index(0);
1419 start_read_all_timer(vc, RA_TIMER);
1423 static void stop_read_all(struct vc_data *vc)
1425 del_timer(&cursor_timer);
1426 cursor_track = prev_cursor_track;
1427 spk_shut_up &= 0xfe;
1428 spk_do_flush();
1431 static void start_read_all_timer(struct vc_data *vc, enum read_all_command command)
1433 struct var_t *cursor_timeout;
1435 cursor_con = vc->vc_num;
1436 read_all_key = command;
1437 cursor_timeout = spk_get_var(CURSOR_TIME);
1438 mod_timer(&cursor_timer,
1439 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1442 static void handle_cursor_read_all(struct vc_data *vc, enum read_all_command command)
1444 int indcount, sentcount, rv, sn;
1446 switch (command) {
1447 case RA_NEXT_SENT:
1448 /* Get Current Sentence */
1449 spk_get_index_count(&indcount, &sentcount);
1450 /*printk("%d %d ", indcount, sentcount); */
1451 spk_reset_index_count(sentcount + 1);
1452 if (indcount == 1) {
1453 if (!say_sentence_num(sentcount + 1, 0)) {
1454 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1455 return;
1457 synth_insert_next_index(0);
1458 } else {
1459 sn = 0;
1460 if (!say_sentence_num(sentcount + 1, 1)) {
1461 sn = 1;
1462 spk_reset_index_count(sn);
1463 } else {
1464 synth_insert_next_index(0);
1466 if (!say_sentence_num(sn, 0)) {
1467 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1468 return;
1470 synth_insert_next_index(0);
1472 start_read_all_timer(vc, RA_TIMER);
1473 break;
1474 case RA_PREV_SENT:
1475 break;
1476 case RA_NEXT_LINE:
1477 read_all_doc(vc);
1478 break;
1479 case RA_PREV_LINE:
1480 break;
1481 case RA_DOWN_ARROW:
1482 if (get_sentence_buf(vc, 0) == -1) {
1483 kbd_fakekey2(vc, RA_DOWN_ARROW);
1484 } else {
1485 say_sentence_num(0, 0);
1486 synth_insert_next_index(0);
1487 start_read_all_timer(vc, RA_TIMER);
1489 break;
1490 case RA_FIND_NEXT_SENT:
1491 rv = get_sentence_buf(vc, 0);
1492 if (rv == -1)
1493 read_all_doc(vc);
1494 if (rv == 0) {
1495 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1496 } else {
1497 say_sentence_num(1, 0);
1498 synth_insert_next_index(0);
1499 start_read_all_timer(vc, RA_TIMER);
1501 break;
1502 case RA_FIND_PREV_SENT:
1503 break;
1504 case RA_TIMER:
1505 spk_get_index_count(&indcount, &sentcount);
1506 if (indcount < 2)
1507 kbd_fakekey2(vc, RA_DOWN_ARROW);
1508 else
1509 start_read_all_timer(vc, RA_TIMER);
1510 break;
1514 static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1516 unsigned long flags;
1518 spin_lock_irqsave(&speakup_info.spinlock, flags);
1519 if (cursor_track == read_all_mode) {
1520 spk_parked &= 0xfe;
1521 if (!synth || up_flag || spk_shut_up) {
1522 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1523 return NOTIFY_STOP;
1525 del_timer(&cursor_timer);
1526 spk_shut_up &= 0xfe;
1527 spk_do_flush();
1528 start_read_all_timer(vc, value + 1);
1529 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1530 return NOTIFY_STOP;
1532 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1533 return NOTIFY_OK;
1536 static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1538 unsigned long flags;
1539 struct var_t *cursor_timeout;
1541 spin_lock_irqsave(&speakup_info.spinlock, flags);
1542 spk_parked &= 0xfe;
1543 if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
1544 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1545 return;
1547 spk_shut_up &= 0xfe;
1548 if (spk_no_intr)
1549 spk_do_flush();
1550 /* the key press flushes if !no_inter but we want to flush on cursor
1551 * moves regardless of no_inter state
1553 is_cursor = value + 1;
1554 old_cursor_pos = vc->vc_pos;
1555 old_cursor_x = vc->state.x;
1556 old_cursor_y = vc->state.y;
1557 speakup_console[vc->vc_num]->ht.cy = vc->state.y;
1558 cursor_con = vc->vc_num;
1559 if (cursor_track == CT_Highlight)
1560 reset_highlight_buffers(vc);
1561 cursor_timeout = spk_get_var(CURSOR_TIME);
1562 mod_timer(&cursor_timer,
1563 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1564 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1567 static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
1569 int i, bi, hi;
1570 int vc_num = vc->vc_num;
1572 bi = (vc->vc_attr & 0x70) >> 4;
1573 hi = speakup_console[vc_num]->ht.highsize[bi];
1575 i = 0;
1576 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1577 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1578 speakup_console[vc_num]->ht.rx[bi] = vc->state.x;
1579 speakup_console[vc_num]->ht.ry[bi] = vc->state.y;
1581 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1582 if (ic[i] > 32) {
1583 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1584 hi++;
1585 } else if ((ic[i] == 32) && (hi != 0)) {
1586 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1587 32) {
1588 speakup_console[vc_num]->ht.highbuf[bi][hi] =
1589 ic[i];
1590 hi++;
1593 i++;
1595 speakup_console[vc_num]->ht.highsize[bi] = hi;
1598 static void reset_highlight_buffers(struct vc_data *vc)
1600 int i;
1601 int vc_num = vc->vc_num;
1603 for (i = 0; i < 8; i++)
1604 speakup_console[vc_num]->ht.highsize[i] = 0;
1607 static int count_highlight_color(struct vc_data *vc)
1609 int i, bg;
1610 int cc;
1611 int vc_num = vc->vc_num;
1612 u16 ch;
1613 u16 *start = (u16 *)vc->vc_origin;
1615 for (i = 0; i < 8; i++)
1616 speakup_console[vc_num]->ht.bgcount[i] = 0;
1618 for (i = 0; i < vc->vc_rows; i++) {
1619 u16 *end = start + vc->vc_cols * 2;
1620 u16 *ptr;
1622 for (ptr = start; ptr < end; ptr++) {
1623 ch = get_attributes(vc, ptr);
1624 bg = (ch & 0x70) >> 4;
1625 speakup_console[vc_num]->ht.bgcount[bg]++;
1627 start += vc->vc_size_row;
1630 cc = 0;
1631 for (i = 0; i < 8; i++)
1632 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1633 cc++;
1634 return cc;
1637 static int get_highlight_color(struct vc_data *vc)
1639 int i, j;
1640 unsigned int cptr[8];
1641 int vc_num = vc->vc_num;
1643 for (i = 0; i < 8; i++)
1644 cptr[i] = i;
1646 for (i = 0; i < 7; i++)
1647 for (j = i + 1; j < 8; j++)
1648 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
1649 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1650 swap(cptr[i], cptr[j]);
1652 for (i = 0; i < 8; i++)
1653 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1654 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1655 return cptr[i];
1656 return -1;
1659 static int speak_highlight(struct vc_data *vc)
1661 int hc, d;
1662 int vc_num = vc->vc_num;
1664 if (count_highlight_color(vc) == 1)
1665 return 0;
1666 hc = get_highlight_color(vc);
1667 if (hc != -1) {
1668 d = vc->state.y - speakup_console[vc_num]->ht.cy;
1669 if ((d == 1) || (d == -1))
1670 if (speakup_console[vc_num]->ht.ry[hc] != vc->state.y)
1671 return 0;
1672 spk_parked |= 0x01;
1673 spk_do_flush();
1674 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
1675 speakup_console[vc_num]->ht.highsize[hc]);
1676 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1677 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1678 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1679 return 1;
1681 return 0;
1684 static void cursor_done(struct timer_list *unused)
1686 struct vc_data *vc = vc_cons[cursor_con].d;
1687 unsigned long flags;
1689 del_timer(&cursor_timer);
1690 spin_lock_irqsave(&speakup_info.spinlock, flags);
1691 if (cursor_con != fg_console) {
1692 is_cursor = 0;
1693 goto out;
1695 speakup_date(vc);
1696 if (win_enabled) {
1697 if (vc->state.x >= win_left && vc->state.x <= win_right &&
1698 vc->state.y >= win_top && vc->state.y <= win_bottom) {
1699 spk_keydown = 0;
1700 is_cursor = 0;
1701 goto out;
1704 if (cursor_track == read_all_mode) {
1705 handle_cursor_read_all(vc, read_all_key);
1706 goto out;
1708 if (cursor_track == CT_Highlight) {
1709 if (speak_highlight(vc)) {
1710 spk_keydown = 0;
1711 is_cursor = 0;
1712 goto out;
1715 if (cursor_track == CT_Window)
1716 speakup_win_say(vc);
1717 else if (is_cursor == 1 || is_cursor == 4)
1718 say_line_from_to(vc, 0, vc->vc_cols, 0);
1719 else
1720 say_char(vc);
1721 spk_keydown = 0;
1722 is_cursor = 0;
1723 out:
1724 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1727 /* called by: vt_notifier_call() */
1728 static void speakup_bs(struct vc_data *vc)
1730 unsigned long flags;
1732 if (!speakup_console[vc->vc_num])
1733 return;
1734 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1735 /* Speakup output, discard */
1736 return;
1737 if (!spk_parked)
1738 speakup_date(vc);
1739 if (spk_shut_up || !synth) {
1740 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1741 return;
1743 if (vc->vc_num == fg_console && spk_keydown) {
1744 spk_keydown = 0;
1745 if (!is_cursor)
1746 say_char(vc);
1748 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1751 /* called by: vt_notifier_call() */
1752 static void speakup_con_write(struct vc_data *vc, u16 *str, int len)
1754 unsigned long flags;
1756 if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
1757 return;
1758 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1759 /* Speakup output, discard */
1760 return;
1761 if (spk_bell_pos && spk_keydown && (vc->state.x == spk_bell_pos - 1))
1762 bleep(3);
1763 if ((is_cursor) || (cursor_track == read_all_mode)) {
1764 if (cursor_track == CT_Highlight)
1765 update_color_buffer(vc, str, len);
1766 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1767 return;
1769 if (win_enabled) {
1770 if (vc->state.x >= win_left && vc->state.x <= win_right &&
1771 vc->state.y >= win_top && vc->state.y <= win_bottom) {
1772 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1773 return;
1777 spkup_write(str, len);
1778 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1781 static void speakup_con_update(struct vc_data *vc)
1783 unsigned long flags;
1785 if (!speakup_console[vc->vc_num] || spk_parked)
1786 return;
1787 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1788 /* Speakup output, discard */
1789 return;
1790 speakup_date(vc);
1791 if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) {
1792 synth_printf("%s", spk_str_pause);
1793 spk_paused = true;
1795 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1798 static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1800 unsigned long flags;
1801 int on_off = 2;
1802 char *label;
1804 if (!synth || up_flag || spk_killed)
1805 return;
1806 spin_lock_irqsave(&speakup_info.spinlock, flags);
1807 spk_shut_up &= 0xfe;
1808 if (spk_no_intr)
1809 spk_do_flush();
1810 switch (value) {
1811 case KVAL(K_CAPS):
1812 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
1813 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
1814 break;
1815 case KVAL(K_NUM):
1816 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
1817 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
1818 break;
1819 case KVAL(K_HOLD):
1820 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
1821 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
1822 if (speakup_console[vc->vc_num])
1823 speakup_console[vc->vc_num]->tty_stopped = on_off;
1824 break;
1825 default:
1826 spk_parked &= 0xfe;
1827 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1828 return;
1830 if (on_off < 2)
1831 synth_printf("%s %s\n",
1832 label, spk_msg_get(MSG_STATUS_START + on_off));
1833 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1836 static int inc_dec_var(u_char value)
1838 struct st_var_header *p_header;
1839 struct var_t *var_data;
1840 char num_buf[32];
1841 char *cp = num_buf;
1842 char *pn;
1843 int var_id = (int)value - VAR_START;
1844 int how = (var_id & 1) ? E_INC : E_DEC;
1846 var_id = var_id / 2 + FIRST_SET_VAR;
1847 p_header = spk_get_var_header(var_id);
1848 if (!p_header)
1849 return -1;
1850 if (p_header->var_type != VAR_NUM)
1851 return -1;
1852 var_data = p_header->data;
1853 if (spk_set_num_var(1, p_header, how) != 0)
1854 return -1;
1855 if (!spk_close_press) {
1856 for (pn = p_header->name; *pn; pn++) {
1857 if (*pn == '_')
1858 *cp = SPACE;
1859 else
1860 *cp++ = *pn;
1863 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
1864 var_data->u.n.value);
1865 synth_printf("%s", num_buf);
1866 return 0;
1869 static void speakup_win_set(struct vc_data *vc)
1871 char info[40];
1873 if (win_start > 1) {
1874 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
1875 return;
1877 if (spk_x < win_left || spk_y < win_top) {
1878 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
1879 return;
1881 if (win_start && spk_x == win_left && spk_y == win_top) {
1882 win_left = 0;
1883 win_right = vc->vc_cols - 1;
1884 win_bottom = spk_y;
1885 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
1886 (int)win_top + 1);
1887 } else {
1888 if (!win_start) {
1889 win_top = spk_y;
1890 win_left = spk_x;
1891 } else {
1892 win_bottom = spk_y;
1893 win_right = spk_x;
1895 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
1896 (win_start) ?
1897 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
1898 (int)spk_y + 1, (int)spk_x + 1);
1900 synth_printf("%s\n", info);
1901 win_start++;
1904 static void speakup_win_clear(struct vc_data *vc)
1906 win_top = 0;
1907 win_bottom = 0;
1908 win_left = 0;
1909 win_right = 0;
1910 win_start = 0;
1911 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
1914 static void speakup_win_enable(struct vc_data *vc)
1916 if (win_start < 2) {
1917 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
1918 return;
1920 win_enabled ^= 1;
1921 if (win_enabled)
1922 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
1923 else
1924 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
1927 static void speakup_bits(struct vc_data *vc)
1929 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
1931 if (spk_special_handler || val < 1 || val > 6) {
1932 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
1933 return;
1935 pb_edit = &spk_punc_info[val];
1936 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1937 spk_special_handler = edit_bits;
1940 static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1942 static u_char goto_buf[8];
1943 static int num;
1944 int maxlen;
1945 char *cp;
1946 u16 wch;
1948 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1949 goto do_goto;
1950 if (type == KT_LATIN && ch == '\n')
1951 goto do_goto;
1952 if (type != 0)
1953 goto oops;
1954 if (ch == 8) {
1955 u16 wch;
1957 if (num == 0)
1958 return -1;
1959 wch = goto_buf[--num];
1960 goto_buf[num] = '\0';
1961 spkup_write(&wch, 1);
1962 return 1;
1964 if (ch < '+' || ch > 'y')
1965 goto oops;
1966 wch = ch;
1967 goto_buf[num++] = ch;
1968 goto_buf[num] = '\0';
1969 spkup_write(&wch, 1);
1970 maxlen = (*goto_buf >= '0') ? 3 : 4;
1971 if ((ch == '+' || ch == '-') && num == 1)
1972 return 1;
1973 if (ch >= '0' && ch <= '9' && num < maxlen)
1974 return 1;
1975 if (num < maxlen - 1 || num > maxlen)
1976 goto oops;
1977 if (ch < 'x' || ch > 'y') {
1978 oops:
1979 if (!spk_killed)
1980 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
1981 goto_buf[num = 0] = '\0';
1982 spk_special_handler = NULL;
1983 return 1;
1986 /* Do not replace with kstrtoul: here we need cp to be updated */
1987 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1989 if (*cp == 'x') {
1990 if (*goto_buf < '0')
1991 goto_pos += spk_x;
1992 else if (goto_pos > 0)
1993 goto_pos--;
1995 if (goto_pos >= vc->vc_cols)
1996 goto_pos = vc->vc_cols - 1;
1997 goto_x = 1;
1998 } else {
1999 if (*goto_buf < '0')
2000 goto_pos += spk_y;
2001 else if (goto_pos > 0)
2002 goto_pos--;
2004 if (goto_pos >= vc->vc_rows)
2005 goto_pos = vc->vc_rows - 1;
2006 goto_x = 0;
2008 goto_buf[num = 0] = '\0';
2009 do_goto:
2010 spk_special_handler = NULL;
2011 spk_parked |= 0x01;
2012 if (goto_x) {
2013 spk_pos -= spk_x * 2;
2014 spk_x = goto_pos;
2015 spk_pos += goto_pos * 2;
2016 say_word(vc);
2017 } else {
2018 spk_y = goto_pos;
2019 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
2020 say_line(vc);
2022 return 1;
2025 static void speakup_goto(struct vc_data *vc)
2027 if (spk_special_handler) {
2028 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
2029 return;
2031 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
2032 spk_special_handler = handle_goto;
2035 static void speakup_help(struct vc_data *vc)
2037 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
2040 static void do_nothing(struct vc_data *vc)
2042 return; /* flush done in do_spkup */
2045 static u_char key_speakup, spk_key_locked;
2047 static void speakup_lock(struct vc_data *vc)
2049 if (!spk_key_locked) {
2050 spk_key_locked = 16;
2051 key_speakup = 16;
2052 } else {
2053 spk_key_locked = 0;
2054 key_speakup = 0;
2058 typedef void (*spkup_hand) (struct vc_data *);
2059 static spkup_hand spkup_handler[] = {
2060 /* must be ordered same as defines in speakup.h */
2061 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2062 speakup_cut, speakup_paste, say_first_char, say_last_char,
2063 say_char, say_prev_char, say_next_char,
2064 say_word, say_prev_word, say_next_word,
2065 say_line, say_prev_line, say_next_line,
2066 top_edge, bottom_edge, left_edge, right_edge,
2067 spell_word, spell_word, say_screen,
2068 say_position, say_attributes,
2069 speakup_off, speakup_parked, say_line, /* this is for indent */
2070 say_from_top, say_to_bottom,
2071 say_from_left, say_to_right,
2072 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2073 speakup_bits, speakup_bits, speakup_bits,
2074 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2075 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2078 static void do_spkup(struct vc_data *vc, u_char value)
2080 if (spk_killed && value != SPEECH_KILL)
2081 return;
2082 spk_keydown = 0;
2083 spk_lastkey = 0;
2084 spk_shut_up &= 0xfe;
2085 this_speakup_key = value;
2086 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
2087 spk_do_flush();
2088 (*spkup_handler[value]) (vc);
2089 } else {
2090 if (inc_dec_var(value) < 0)
2091 bleep(9);
2095 static const char *pad_chars = "0123456789+-*/\015,.?()";
2097 static int
2098 speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
2099 int up_flag)
2101 unsigned long flags;
2102 int kh;
2103 u_char *key_info;
2104 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2105 u_char shift_info, offset;
2106 int ret = 0;
2108 if (!synth)
2109 return 0;
2111 spin_lock_irqsave(&speakup_info.spinlock, flags);
2112 tty = vc->port.tty;
2113 if (type >= 0xf0)
2114 type -= 0xf0;
2115 if (type == KT_PAD &&
2116 (vt_get_leds(fg_console, VC_NUMLOCK))) {
2117 if (up_flag) {
2118 spk_keydown = 0;
2119 goto out;
2121 value = pad_chars[value];
2122 spk_lastkey = value;
2123 spk_keydown++;
2124 spk_parked &= 0xfe;
2125 goto no_map;
2127 if (keycode >= MAX_KEY)
2128 goto no_map;
2129 key_info = spk_our_keys[keycode];
2130 if (!key_info)
2131 goto no_map;
2132 /* Check valid read all mode keys */
2133 if ((cursor_track == read_all_mode) && (!up_flag)) {
2134 switch (value) {
2135 case KVAL(K_DOWN):
2136 case KVAL(K_UP):
2137 case KVAL(K_LEFT):
2138 case KVAL(K_RIGHT):
2139 case KVAL(K_PGUP):
2140 case KVAL(K_PGDN):
2141 break;
2142 default:
2143 stop_read_all(vc);
2144 break;
2147 shift_info = (shift_state & 0x0f) + key_speakup;
2148 offset = spk_shift_table[shift_info];
2149 if (offset) {
2150 new_key = key_info[offset];
2151 if (new_key) {
2152 ret = 1;
2153 if (new_key == SPK_KEY) {
2154 if (!spk_key_locked)
2155 key_speakup = (up_flag) ? 0 : 16;
2156 if (up_flag || spk_killed)
2157 goto out;
2158 spk_shut_up &= 0xfe;
2159 spk_do_flush();
2160 goto out;
2162 if (up_flag)
2163 goto out;
2164 if (last_keycode == keycode &&
2165 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
2166 spk_close_press = 1;
2167 offset = spk_shift_table[shift_info + 32];
2168 /* double press? */
2169 if (offset && key_info[offset])
2170 new_key = key_info[offset];
2172 last_keycode = keycode;
2173 last_spk_jiffy = jiffies;
2174 type = KT_SPKUP;
2175 value = new_key;
2178 no_map:
2179 if (type == KT_SPKUP && !spk_special_handler) {
2180 do_spkup(vc, new_key);
2181 spk_close_press = 0;
2182 ret = 1;
2183 goto out;
2185 if (up_flag || spk_killed || type == KT_SHIFT)
2186 goto out;
2187 spk_shut_up &= 0xfe;
2188 kh = (value == KVAL(K_DOWN)) ||
2189 (value == KVAL(K_UP)) ||
2190 (value == KVAL(K_LEFT)) ||
2191 (value == KVAL(K_RIGHT));
2192 if ((cursor_track != read_all_mode) || !kh)
2193 if (!spk_no_intr)
2194 spk_do_flush();
2195 if (spk_special_handler) {
2196 if (type == KT_SPEC && value == 1) {
2197 value = '\n';
2198 type = KT_LATIN;
2199 } else if (type == KT_LETTER) {
2200 type = KT_LATIN;
2201 } else if (value == 0x7f) {
2202 value = 8; /* make del = backspace */
2204 ret = (*spk_special_handler) (vc, type, value, keycode);
2205 spk_close_press = 0;
2206 if (ret < 0)
2207 bleep(9);
2208 goto out;
2210 last_keycode = 0;
2211 out:
2212 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2213 return ret;
2216 static int keyboard_notifier_call(struct notifier_block *nb,
2217 unsigned long code, void *_param)
2219 struct keyboard_notifier_param *param = _param;
2220 struct vc_data *vc = param->vc;
2221 int up = !param->down;
2222 int ret = NOTIFY_OK;
2223 static int keycode; /* to hold the current keycode */
2225 in_keyboard_notifier = 1;
2227 if (vc->vc_mode == KD_GRAPHICS)
2228 goto out;
2231 * First, determine whether we are handling a fake keypress on
2232 * the current processor. If we are, then return NOTIFY_OK,
2233 * to pass the keystroke up the chain. This prevents us from
2234 * trying to take the Speakup lock while it is held by the
2235 * processor on which the simulated keystroke was generated.
2236 * Also, the simulated keystrokes should be ignored by Speakup.
2239 if (speakup_fake_key_pressed())
2240 goto out;
2242 switch (code) {
2243 case KBD_KEYCODE:
2244 /* speakup requires keycode and keysym currently */
2245 keycode = param->value;
2246 break;
2247 case KBD_UNBOUND_KEYCODE:
2248 /* not used yet */
2249 break;
2250 case KBD_UNICODE:
2251 /* not used yet */
2252 break;
2253 case KBD_KEYSYM:
2254 if (speakup_key(vc, param->shift, keycode, param->value, up))
2255 ret = NOTIFY_STOP;
2256 else if (KTYP(param->value) == KT_CUR)
2257 ret = pre_handle_cursor(vc, KVAL(param->value), up);
2258 break;
2259 case KBD_POST_KEYSYM:{
2260 unsigned char type = KTYP(param->value) - 0xf0;
2261 unsigned char val = KVAL(param->value);
2263 switch (type) {
2264 case KT_SHIFT:
2265 do_handle_shift(vc, val, up);
2266 break;
2267 case KT_LATIN:
2268 case KT_LETTER:
2269 do_handle_latin(vc, val, up);
2270 break;
2271 case KT_CUR:
2272 do_handle_cursor(vc, val, up);
2273 break;
2274 case KT_SPEC:
2275 do_handle_spec(vc, val, up);
2276 break;
2278 break;
2281 out:
2282 in_keyboard_notifier = 0;
2283 return ret;
2286 static int vt_notifier_call(struct notifier_block *nb,
2287 unsigned long code, void *_param)
2289 struct vt_notifier_param *param = _param;
2290 struct vc_data *vc = param->vc;
2292 switch (code) {
2293 case VT_ALLOCATE:
2294 if (vc->vc_mode == KD_TEXT)
2295 speakup_allocate(vc, GFP_ATOMIC);
2296 break;
2297 case VT_DEALLOCATE:
2298 speakup_deallocate(vc);
2299 break;
2300 case VT_WRITE:
2301 if (param->c == '\b') {
2302 speakup_bs(vc);
2303 } else {
2304 u16 d = param->c;
2306 speakup_con_write(vc, &d, 1);
2308 break;
2309 case VT_UPDATE:
2310 speakup_con_update(vc);
2311 break;
2313 return NOTIFY_OK;
2316 /* called by: module_exit() */
2317 static void __exit speakup_exit(void)
2319 int i;
2321 unregister_keyboard_notifier(&keyboard_notifier_block);
2322 unregister_vt_notifier(&vt_notifier_block);
2323 speakup_unregister_devsynth();
2324 speakup_cancel_selection();
2325 speakup_cancel_paste();
2326 del_timer_sync(&cursor_timer);
2327 kthread_stop(speakup_task);
2328 speakup_task = NULL;
2329 mutex_lock(&spk_mutex);
2330 synth_release();
2331 mutex_unlock(&spk_mutex);
2332 spk_ttyio_unregister_ldisc();
2334 speakup_kobj_exit();
2336 for (i = 0; i < MAX_NR_CONSOLES; i++)
2337 kfree(speakup_console[i]);
2339 speakup_remove_virtual_keyboard();
2341 for (i = 0; i < MAXVARS; i++)
2342 speakup_unregister_var(i);
2344 for (i = 0; i < 256; i++) {
2345 if (spk_characters[i] != spk_default_chars[i])
2346 kfree(spk_characters[i]);
2349 spk_free_user_msgs();
2352 /* call by: module_init() */
2353 static int __init speakup_init(void)
2355 int i;
2356 long err = 0;
2357 struct vc_data *vc = vc_cons[fg_console].d;
2358 struct var_t *var;
2360 /* These first few initializations cannot fail. */
2361 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2362 spk_reset_default_chars();
2363 spk_reset_default_chartab();
2364 spk_strlwr(synth_name);
2365 spk_vars[0].u.n.high = vc->vc_cols;
2366 for (var = spk_vars; var->var_id != MAXVARS; var++)
2367 speakup_register_var(var);
2368 for (var = synth_time_vars;
2369 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
2370 speakup_register_var(var);
2371 for (i = 1; spk_punc_info[i].mask != 0; i++)
2372 spk_set_mask_bits(NULL, i, 2);
2374 spk_set_key_info(spk_key_defaults, spk_key_buf);
2376 /* From here on out, initializations can fail. */
2377 err = speakup_add_virtual_keyboard();
2378 if (err)
2379 goto error_virtkeyboard;
2381 for (i = 0; i < MAX_NR_CONSOLES; i++)
2382 if (vc_cons[i].d) {
2383 err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
2384 if (err)
2385 goto error_kobjects;
2388 if (spk_quiet_boot)
2389 spk_shut_up |= 0x01;
2391 err = speakup_kobj_init();
2392 if (err)
2393 goto error_kobjects;
2395 spk_ttyio_register_ldisc();
2396 synth_init(synth_name);
2397 speakup_register_devsynth();
2399 * register_devsynth might fail, but this error is not fatal.
2400 * /dev/synth is an extra feature; the rest of Speakup
2401 * will work fine without it.
2404 err = register_keyboard_notifier(&keyboard_notifier_block);
2405 if (err)
2406 goto error_kbdnotifier;
2407 err = register_vt_notifier(&vt_notifier_block);
2408 if (err)
2409 goto error_vtnotifier;
2411 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
2413 if (IS_ERR(speakup_task)) {
2414 err = PTR_ERR(speakup_task);
2415 goto error_task;
2418 set_user_nice(speakup_task, 10);
2419 wake_up_process(speakup_task);
2421 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2422 pr_info("synth name on entry is: %s\n", synth_name);
2423 goto out;
2425 error_task:
2426 unregister_vt_notifier(&vt_notifier_block);
2428 error_vtnotifier:
2429 unregister_keyboard_notifier(&keyboard_notifier_block);
2430 del_timer(&cursor_timer);
2432 error_kbdnotifier:
2433 speakup_unregister_devsynth();
2434 mutex_lock(&spk_mutex);
2435 synth_release();
2436 mutex_unlock(&spk_mutex);
2437 speakup_kobj_exit();
2439 error_kobjects:
2440 for (i = 0; i < MAX_NR_CONSOLES; i++)
2441 kfree(speakup_console[i]);
2443 speakup_remove_virtual_keyboard();
2445 error_virtkeyboard:
2446 for (i = 0; i < MAXVARS; i++)
2447 speakup_unregister_var(i);
2449 for (i = 0; i < 256; i++) {
2450 if (spk_characters[i] != spk_default_chars[i])
2451 kfree(spk_characters[i]);
2454 spk_free_user_msgs();
2456 out:
2457 return err;
2460 module_init(speakup_init);
2461 module_exit(speakup_exit);