Linux 4.19.133
[linux/fpc-iii.git] / drivers / staging / speakup / main.c
blobd4a74f7ddf6b1925f4fc29ab17c26bc8d5ad35ac
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 /* Speakup Cursor Track Variables */
94 static int cursor_track = 1, prev_cursor_track = 1;
96 /* cursor track modes, must be ordered same as cursor_msgs */
97 enum {
98 CT_Off = 0,
99 CT_On,
100 CT_Highlight,
101 CT_Window,
102 CT_Max
105 #define read_all_mode CT_Max
107 static struct tty_struct *tty;
109 static void spkup_write(const u16 *in_buf, int count);
111 static char *phonetic[] = {
112 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
113 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
114 "papa",
115 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
116 "x ray", "yankee", "zulu"
119 /* array of 256 char pointers (one for each character description)
120 * initialized to default_chars and user selectable via
121 * /proc/speakup/characters
123 char *spk_characters[256];
125 char *spk_default_chars[256] = {
126 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
127 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
128 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
129 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
130 "control",
131 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
132 "tick",
133 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
134 "dot",
135 "slash",
136 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
137 "eight", "nine",
138 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
139 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
140 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
141 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
142 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
143 "caret",
144 "line",
145 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
146 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
147 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
148 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
149 /*127*/ "del", "control", "control", "control", "control", "control",
150 "control", "control", "control", "control", "control",
151 /*138*/ "control", "control", "control", "control", "control",
152 "control", "control", "control", "control", "control",
153 "control", "control",
154 /*150*/ "control", "control", "control", "control", "control",
155 "control", "control", "control", "control", "control",
156 /*160*/ "nbsp", "inverted bang",
157 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
158 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
159 /*172*/ "not", "soft hyphen", "registered", "macron",
160 /*176*/ "degrees", "plus or minus", "super two", "super three",
161 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
162 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
163 /*188*/ "one quarter", "one half", "three quarters",
164 "inverted question",
165 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
166 "A RING",
167 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
168 "E OOMLAUT",
169 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
170 "N TILDE",
171 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
172 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
173 "U CIRCUMFLEX",
174 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
175 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
176 /*230*/ "ae", "c cidella", "e grave", "e acute",
177 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
178 "i circumflex",
179 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
180 "o circumflex",
181 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
182 "u acute",
183 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
186 /* array of 256 u_short (one for each character)
187 * initialized to default_chartab and user selectable via
188 * /sys/module/speakup/parameters/chartab
190 u_short spk_chartab[256];
192 static u_short default_chartab[256] = {
193 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
194 B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
195 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
196 B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */
197 WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */
198 PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */
199 NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */
200 NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */
201 PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */
202 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */
203 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */
204 A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */
205 PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */
206 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */
207 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */
208 ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */
209 B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
210 B_SYM, /* 135 */
211 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
212 B_CAPSYM, /* 143 */
213 B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
214 B_SYM, /* 151 */
215 B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
216 B_SYM, /* 159 */
217 WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
218 B_SYM, /* 167 */
219 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */
220 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */
221 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */
222 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */
223 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */
224 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */
225 A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */
226 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */
227 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */
228 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */
229 ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */
232 struct task_struct *speakup_task;
233 struct bleep spk_unprocessed_sound;
234 static int spk_keydown;
235 static u16 spk_lastkey;
236 static u_char spk_close_press, keymap_flags;
237 static u_char last_keycode, this_speakup_key;
238 static u_long last_spk_jiffy;
240 struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
242 DEFINE_MUTEX(spk_mutex);
244 static int keyboard_notifier_call(struct notifier_block *,
245 unsigned long code, void *param);
247 static struct notifier_block keyboard_notifier_block = {
248 .notifier_call = keyboard_notifier_call,
251 static int vt_notifier_call(struct notifier_block *,
252 unsigned long code, void *param);
254 static struct notifier_block vt_notifier_block = {
255 .notifier_call = vt_notifier_call,
258 static unsigned char get_attributes(struct vc_data *vc, u16 *pos)
260 pos = screen_pos(vc, pos - (u16 *)vc->vc_origin, 1);
261 return (scr_readw(pos) & ~vc->vc_hi_font_mask) >> 8;
264 static void speakup_date(struct vc_data *vc)
266 spk_x = spk_cx = vc->vc_x;
267 spk_y = spk_cy = vc->vc_y;
268 spk_pos = spk_cp = vc->vc_pos;
269 spk_old_attr = spk_attr;
270 spk_attr = get_attributes(vc, (u_short *)spk_pos);
273 static void bleep(u_short val)
275 static const short vals[] = {
276 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
278 short freq;
279 int time = spk_bleep_time;
281 freq = vals[val % 12];
282 if (val > 11)
283 freq *= (1 << (val / 12));
284 spk_unprocessed_sound.freq = freq;
285 spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
286 spk_unprocessed_sound.active = 1;
287 /* We can only have 1 active sound at a time. */
290 static void speakup_shut_up(struct vc_data *vc)
292 if (spk_killed)
293 return;
294 spk_shut_up |= 0x01;
295 spk_parked &= 0xfe;
296 speakup_date(vc);
297 if (synth)
298 spk_do_flush();
301 static void speech_kill(struct vc_data *vc)
303 char val = synth->is_alive(synth);
305 if (val == 0)
306 return;
308 /* re-enables synth, if disabled */
309 if (val == 2 || spk_killed) {
310 /* dead */
311 spk_shut_up &= ~0x40;
312 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
313 } else {
314 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
315 spk_shut_up |= 0x40;
319 static void speakup_off(struct vc_data *vc)
321 if (spk_shut_up & 0x80) {
322 spk_shut_up &= 0x7f;
323 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
324 } else {
325 spk_shut_up |= 0x80;
326 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
328 speakup_date(vc);
331 static void speakup_parked(struct vc_data *vc)
333 if (spk_parked & 0x80) {
334 spk_parked = 0;
335 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
336 } else {
337 spk_parked |= 0x80;
338 synth_printf("%s\n", spk_msg_get(MSG_PARKED));
342 static void speakup_cut(struct vc_data *vc)
344 static const char err_buf[] = "set selection failed";
345 int ret;
347 if (!mark_cut_flag) {
348 mark_cut_flag = 1;
349 spk_xs = (u_short)spk_x;
350 spk_ys = (u_short)spk_y;
351 spk_sel_cons = vc;
352 synth_printf("%s\n", spk_msg_get(MSG_MARK));
353 return;
355 spk_xe = (u_short)spk_x;
356 spk_ye = (u_short)spk_y;
357 mark_cut_flag = 0;
358 synth_printf("%s\n", spk_msg_get(MSG_CUT));
360 speakup_clear_selection();
361 ret = speakup_set_selection(tty);
363 switch (ret) {
364 case 0:
365 break; /* no error */
366 case -EFAULT:
367 pr_warn("%sEFAULT\n", err_buf);
368 break;
369 case -EINVAL:
370 pr_warn("%sEINVAL\n", err_buf);
371 break;
372 case -ENOMEM:
373 pr_warn("%sENOMEM\n", err_buf);
374 break;
378 static void speakup_paste(struct vc_data *vc)
380 if (mark_cut_flag) {
381 mark_cut_flag = 0;
382 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
383 } else {
384 synth_printf("%s\n", spk_msg_get(MSG_PASTE));
385 speakup_paste_selection(tty);
389 static void say_attributes(struct vc_data *vc)
391 int fg = spk_attr & 0x0f;
392 int bg = spk_attr >> 4;
394 if (fg > 8) {
395 synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
396 fg -= 8;
398 synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
399 if (bg > 7) {
400 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
401 bg -= 8;
402 } else {
403 synth_printf(" %s ", spk_msg_get(MSG_ON));
405 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
408 enum {
409 edge_top = 1,
410 edge_bottom,
411 edge_left,
412 edge_right,
413 edge_quiet
416 static void announce_edge(struct vc_data *vc, int 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, 1);
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 u_short edge_said = 0, last_state = 0, state = 0;
613 spk_parked |= 0x01;
615 if (spk_x == 0) {
616 if (spk_y == 0) {
617 announce_edge(vc, edge_top);
618 return;
620 spk_y--;
621 spk_x = vc->vc_cols;
622 edge_said = edge_quiet;
624 while (1) {
625 if (spk_x == 0) {
626 if (spk_y == 0) {
627 edge_said = edge_top;
628 break;
630 if (edge_said != edge_quiet)
631 edge_said = edge_left;
632 if (state > 0)
633 break;
634 spk_y--;
635 spk_x = vc->vc_cols - 1;
636 } else {
637 spk_x--;
639 spk_pos -= 2;
640 ch = get_char(vc, (u_short *)spk_pos, &temp);
641 if (ch == SPACE || ch == 0)
642 state = 0;
643 else if (ch < 0x100 && IS_WDLM(ch))
644 state = 1;
645 else
646 state = 2;
647 if (state < last_state) {
648 spk_pos += 2;
649 spk_x++;
650 break;
652 last_state = state;
654 if (spk_x == 0 && edge_said == edge_quiet)
655 edge_said = edge_left;
656 if (edge_said > 0 && edge_said < edge_quiet)
657 announce_edge(vc, edge_said);
658 say_word(vc);
661 static void say_next_word(struct vc_data *vc)
663 u_char temp;
664 u16 ch;
665 u_short edge_said = 0, last_state = 2, state = 0;
667 spk_parked |= 0x01;
668 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
669 announce_edge(vc, edge_bottom);
670 return;
672 while (1) {
673 ch = get_char(vc, (u_short *)spk_pos, &temp);
674 if (ch == SPACE || ch == 0)
675 state = 0;
676 else if (ch < 0x100 && IS_WDLM(ch))
677 state = 1;
678 else
679 state = 2;
680 if (state > last_state)
681 break;
682 if (spk_x >= vc->vc_cols - 1) {
683 if (spk_y == vc->vc_rows - 1) {
684 edge_said = edge_bottom;
685 break;
687 state = 0;
688 spk_y++;
689 spk_x = 0;
690 edge_said = edge_right;
691 } else {
692 spk_x++;
694 spk_pos += 2;
695 last_state = state;
697 if (edge_said > 0)
698 announce_edge(vc, edge_said);
699 say_word(vc);
702 static void spell_word(struct vc_data *vc)
704 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
705 u16 *cp = buf;
706 char *cp1;
707 char *str_cap = spk_str_caps_stop;
708 char *last_cap = spk_str_caps_stop;
709 struct var_t *direct = spk_get_var(DIRECT);
710 u16 ch;
712 if (!get_word(vc))
713 return;
714 while ((ch = *cp)) {
715 if (cp != buf)
716 synth_printf(" %s ", delay_str[spk_spell_delay]);
717 /* FIXME: Non-latin1 considered as lower case */
718 if (ch < 0x100 && IS_CHAR(ch, B_CAP)) {
719 str_cap = spk_str_caps_start;
720 if (*spk_str_caps_stop)
721 spk_pitch_shift++;
722 else /* synth has no pitch */
723 last_cap = spk_str_caps_stop;
724 } else {
725 str_cap = spk_str_caps_stop;
727 if (str_cap != last_cap) {
728 synth_printf("%s", str_cap);
729 last_cap = str_cap;
731 if (ch >= 0x100 || (direct && direct->u.n.value)) {
732 synth_putwc_s(ch);
733 } else if (this_speakup_key == SPELL_PHONETIC &&
734 ch <= 0x7f && isalpha(ch)) {
735 ch &= 0x1f;
736 cp1 = phonetic[--ch];
737 synth_printf("%s", cp1);
738 } else {
739 cp1 = spk_characters[ch];
740 if (*cp1 == '^') {
741 synth_printf("%s", spk_msg_get(MSG_CTRL));
742 cp1++;
744 synth_printf("%s", cp1);
746 cp++;
748 if (str_cap != spk_str_caps_stop)
749 synth_printf("%s", spk_str_caps_stop);
752 static int get_line(struct vc_data *vc)
754 u_long tmp = spk_pos - (spk_x * 2);
755 int i = 0;
756 u_char tmp2;
758 spk_old_attr = spk_attr;
759 spk_attr = get_attributes(vc, (u_short *)spk_pos);
760 for (i = 0; i < vc->vc_cols; i++) {
761 buf[i] = get_char(vc, (u_short *)tmp, &tmp2);
762 tmp += 2;
764 for (--i; i >= 0; i--)
765 if (buf[i] != SPACE)
766 break;
767 return ++i;
770 static void say_line(struct vc_data *vc)
772 int i = get_line(vc);
773 u16 *cp;
774 u_short saved_punc_mask = spk_punc_mask;
776 if (i == 0) {
777 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
778 return;
780 buf[i++] = '\n';
781 if (this_speakup_key == SAY_LINE_INDENT) {
782 cp = buf;
783 while (*cp == SPACE)
784 cp++;
785 synth_printf("%zd, ", (cp - buf) + 1);
787 spk_punc_mask = spk_punc_masks[spk_reading_punc];
788 spkup_write(buf, i);
789 spk_punc_mask = saved_punc_mask;
792 static void say_prev_line(struct vc_data *vc)
794 spk_parked |= 0x01;
795 if (spk_y == 0) {
796 announce_edge(vc, edge_top);
797 return;
799 spk_y--;
800 spk_pos -= vc->vc_size_row;
801 say_line(vc);
804 static void say_next_line(struct vc_data *vc)
806 spk_parked |= 0x01;
807 if (spk_y == vc->vc_rows - 1) {
808 announce_edge(vc, edge_bottom);
809 return;
811 spk_y++;
812 spk_pos += vc->vc_size_row;
813 say_line(vc);
816 static int say_from_to(struct vc_data *vc, u_long from, u_long to,
817 int read_punc)
819 int i = 0;
820 u_char tmp;
821 u_short saved_punc_mask = spk_punc_mask;
823 spk_old_attr = spk_attr;
824 spk_attr = get_attributes(vc, (u_short *)from);
825 while (from < to) {
826 buf[i++] = get_char(vc, (u_short *)from, &tmp);
827 from += 2;
828 if (i >= vc->vc_size_row)
829 break;
831 for (--i; i >= 0; i--)
832 if (buf[i] != SPACE)
833 break;
834 buf[++i] = SPACE;
835 buf[++i] = '\0';
836 if (i < 1)
837 return i;
838 if (read_punc)
839 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
840 spkup_write(buf, i);
841 if (read_punc)
842 spk_punc_mask = saved_punc_mask;
843 return i - 1;
846 static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
847 int read_punc)
849 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
850 u_long end = start + (to * 2);
852 start += from * 2;
853 if (say_from_to(vc, start, end, read_punc) <= 0)
854 if (cursor_track != read_all_mode)
855 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
858 /* Sentence Reading Commands */
860 static int currsentence;
861 static int numsentences[2];
862 static u16 *sentbufend[2];
863 static u16 *sentmarks[2][10];
864 static int currbuf;
865 static int bn;
866 static u16 sentbuf[2][256];
868 static int say_sentence_num(int num, int prev)
870 bn = currbuf;
871 currsentence = num + 1;
872 if (prev && --bn == -1)
873 bn = 1;
875 if (num > numsentences[bn])
876 return 0;
878 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
879 return 1;
882 static int get_sentence_buf(struct vc_data *vc, int read_punc)
884 u_long start, end;
885 int i, bn;
886 u_char tmp;
888 currbuf++;
889 if (currbuf == 2)
890 currbuf = 0;
891 bn = currbuf;
892 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
893 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
895 numsentences[bn] = 0;
896 sentmarks[bn][0] = &sentbuf[bn][0];
897 i = 0;
898 spk_old_attr = spk_attr;
899 spk_attr = get_attributes(vc, (u_short *)start);
901 while (start < end) {
902 sentbuf[bn][i] = get_char(vc, (u_short *)start, &tmp);
903 if (i > 0) {
904 if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.' &&
905 numsentences[bn] < 9) {
906 /* Sentence Marker */
907 numsentences[bn]++;
908 sentmarks[bn][numsentences[bn]] =
909 &sentbuf[bn][i];
912 i++;
913 start += 2;
914 if (i >= vc->vc_size_row)
915 break;
918 for (--i; i >= 0; i--)
919 if (sentbuf[bn][i] != SPACE)
920 break;
922 if (i < 1)
923 return -1;
925 sentbuf[bn][++i] = SPACE;
926 sentbuf[bn][++i] = '\0';
928 sentbufend[bn] = &sentbuf[bn][i];
929 return numsentences[bn];
932 static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
934 u_long start = vc->vc_origin, end;
936 if (from > 0)
937 start += from * vc->vc_size_row;
938 if (to > vc->vc_rows)
939 to = vc->vc_rows;
940 end = vc->vc_origin + (to * vc->vc_size_row);
941 for (from = start; from < end; from = to) {
942 to = from + vc->vc_size_row;
943 say_from_to(vc, from, to, 1);
947 static void say_screen(struct vc_data *vc)
949 say_screen_from_to(vc, 0, vc->vc_rows);
952 static void speakup_win_say(struct vc_data *vc)
954 u_long start, end, from, to;
956 if (win_start < 2) {
957 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
958 return;
960 start = vc->vc_origin + (win_top * vc->vc_size_row);
961 end = vc->vc_origin + (win_bottom * vc->vc_size_row);
962 while (start <= end) {
963 from = start + (win_left * 2);
964 to = start + (win_right * 2);
965 say_from_to(vc, from, to, 1);
966 start += vc->vc_size_row;
970 static void top_edge(struct vc_data *vc)
972 spk_parked |= 0x01;
973 spk_pos = vc->vc_origin + 2 * spk_x;
974 spk_y = 0;
975 say_line(vc);
978 static void bottom_edge(struct vc_data *vc)
980 spk_parked |= 0x01;
981 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
982 spk_y = vc->vc_rows - 1;
983 say_line(vc);
986 static void left_edge(struct vc_data *vc)
988 spk_parked |= 0x01;
989 spk_pos -= spk_x * 2;
990 spk_x = 0;
991 say_char(vc);
994 static void right_edge(struct vc_data *vc)
996 spk_parked |= 0x01;
997 spk_pos += (vc->vc_cols - spk_x - 1) * 2;
998 spk_x = vc->vc_cols - 1;
999 say_char(vc);
1002 static void say_first_char(struct vc_data *vc)
1004 int i, len = get_line(vc);
1005 u16 ch;
1007 spk_parked |= 0x01;
1008 if (len == 0) {
1009 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1010 return;
1012 for (i = 0; i < len; i++)
1013 if (buf[i] != SPACE)
1014 break;
1015 ch = buf[i];
1016 spk_pos -= (spk_x - i) * 2;
1017 spk_x = i;
1018 synth_printf("%d, ", ++i);
1019 speak_char(ch);
1022 static void say_last_char(struct vc_data *vc)
1024 int len = get_line(vc);
1025 u16 ch;
1027 spk_parked |= 0x01;
1028 if (len == 0) {
1029 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
1030 return;
1032 ch = buf[--len];
1033 spk_pos -= (spk_x - len) * 2;
1034 spk_x = len;
1035 synth_printf("%d, ", ++len);
1036 speak_char(ch);
1039 static void say_position(struct vc_data *vc)
1041 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
1042 vc->vc_num + 1);
1043 synth_printf("\n");
1046 /* Added by brianb */
1047 static void say_char_num(struct vc_data *vc)
1049 u_char tmp;
1050 u16 ch = get_char(vc, (u_short *)spk_pos, &tmp);
1052 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
1055 /* these are stub functions to keep keyboard.c happy. */
1057 static void say_from_top(struct vc_data *vc)
1059 say_screen_from_to(vc, 0, spk_y);
1062 static void say_to_bottom(struct vc_data *vc)
1064 say_screen_from_to(vc, spk_y, vc->vc_rows);
1067 static void say_from_left(struct vc_data *vc)
1069 say_line_from_to(vc, 0, spk_x, 1);
1072 static void say_to_right(struct vc_data *vc)
1074 say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1077 /* end of stub functions. */
1079 static void spkup_write(const u16 *in_buf, int count)
1081 static int rep_count;
1082 static u16 ch = '\0', old_ch = '\0';
1083 static u_short char_type, last_type;
1084 int in_count = count;
1086 spk_keydown = 0;
1087 while (count--) {
1088 if (cursor_track == read_all_mode) {
1089 /* Insert Sentence Index */
1090 if ((in_buf == sentmarks[bn][currsentence]) &&
1091 (currsentence <= numsentences[bn]))
1092 synth_insert_next_index(currsentence++);
1094 ch = *in_buf++;
1095 if (ch < 0x100)
1096 char_type = spk_chartab[ch];
1097 else
1098 char_type = ALPHA;
1099 if (ch == old_ch && !(char_type & B_NUM)) {
1100 if (++rep_count > 2)
1101 continue;
1102 } else {
1103 if ((last_type & CH_RPT) && rep_count > 2) {
1104 synth_printf(" ");
1105 synth_printf(spk_msg_get(MSG_REPEAT_DESC),
1106 ++rep_count);
1107 synth_printf(" ");
1109 rep_count = 0;
1111 if (ch == spk_lastkey) {
1112 rep_count = 0;
1113 if (spk_key_echo == 1 && ch >= MINECHOCHAR)
1114 speak_char(ch);
1115 } else if (char_type & B_ALPHA) {
1116 if ((synth_flags & SF_DEC) && (last_type & PUNC))
1117 synth_buffer_add(SPACE);
1118 synth_putwc_s(ch);
1119 } else if (char_type & B_NUM) {
1120 rep_count = 0;
1121 synth_putwc_s(ch);
1122 } else if (char_type & spk_punc_mask) {
1123 speak_char(ch);
1124 char_type &= ~PUNC; /* for dec nospell processing */
1125 } else if (char_type & SYNTH_OK) {
1126 /* these are usually puncts like . and , which synth
1127 * needs for expression.
1128 * suppress multiple to get rid of long pauses and
1129 * clear repeat count
1130 * so if someone has
1131 * repeats on you don't get nothing repeated count
1133 if (ch != old_ch)
1134 synth_putwc_s(ch);
1135 else
1136 rep_count = 0;
1137 } else {
1138 /* send space and record position, if next is num overwrite space */
1139 if (old_ch != ch)
1140 synth_buffer_add(SPACE);
1141 else
1142 rep_count = 0;
1144 old_ch = ch;
1145 last_type = char_type;
1147 spk_lastkey = 0;
1148 if (in_count > 2 && rep_count > 2) {
1149 if (last_type & CH_RPT) {
1150 synth_printf(" ");
1151 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1152 ++rep_count);
1153 synth_printf(" ");
1155 rep_count = 0;
1159 static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1161 static void read_all_doc(struct vc_data *vc);
1162 static void cursor_done(struct timer_list *unused);
1163 static DEFINE_TIMER(cursor_timer, cursor_done);
1165 static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1167 unsigned long flags;
1169 if (!synth || up_flag || spk_killed)
1170 return;
1171 spin_lock_irqsave(&speakup_info.spinlock, flags);
1172 if (cursor_track == read_all_mode) {
1173 switch (value) {
1174 case KVAL(K_SHIFT):
1175 del_timer(&cursor_timer);
1176 spk_shut_up &= 0xfe;
1177 spk_do_flush();
1178 read_all_doc(vc);
1179 break;
1180 case KVAL(K_CTRL):
1181 del_timer(&cursor_timer);
1182 cursor_track = prev_cursor_track;
1183 spk_shut_up &= 0xfe;
1184 spk_do_flush();
1185 break;
1187 } else {
1188 spk_shut_up &= 0xfe;
1189 spk_do_flush();
1191 if (spk_say_ctrl && value < NUM_CTL_LABELS)
1192 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
1193 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1196 static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1198 unsigned long flags;
1200 spin_lock_irqsave(&speakup_info.spinlock, flags);
1201 if (up_flag) {
1202 spk_lastkey = 0;
1203 spk_keydown = 0;
1204 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1205 return;
1207 if (!synth || spk_killed) {
1208 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1209 return;
1211 spk_shut_up &= 0xfe;
1212 spk_lastkey = value;
1213 spk_keydown++;
1214 spk_parked &= 0xfe;
1215 if (spk_key_echo == 2 && value >= MINECHOCHAR)
1216 speak_char(value);
1217 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1220 int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
1222 int i = 0, states, key_data_len;
1223 const u_char *cp = key_info;
1224 u_char *cp1 = k_buffer;
1225 u_char ch, version, num_keys;
1227 version = *cp++;
1228 if (version != KEY_MAP_VER) {
1229 pr_debug("version found %d should be %d\n",
1230 version, KEY_MAP_VER);
1231 return -EINVAL;
1233 num_keys = *cp;
1234 states = (int)cp[1];
1235 key_data_len = (states + 1) * (num_keys + 1);
1236 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf)) {
1237 pr_debug("too many key_infos (%d over %u)\n",
1238 key_data_len + SHIFT_TBL_SIZE + 4, (unsigned int)(sizeof(spk_key_buf)));
1239 return -EINVAL;
1241 memset(k_buffer, 0, SHIFT_TBL_SIZE);
1242 memset(spk_our_keys, 0, sizeof(spk_our_keys));
1243 spk_shift_table = k_buffer;
1244 spk_our_keys[0] = spk_shift_table;
1245 cp1 += SHIFT_TBL_SIZE;
1246 memcpy(cp1, cp, key_data_len + 3);
1247 /* get num_keys, states and data */
1248 cp1 += 2; /* now pointing at shift states */
1249 for (i = 1; i <= states; i++) {
1250 ch = *cp1++;
1251 if (ch >= SHIFT_TBL_SIZE) {
1252 pr_debug("(%d) not valid shift state (max_allowed = %d)\n", ch,
1253 SHIFT_TBL_SIZE);
1254 return -EINVAL;
1256 spk_shift_table[ch] = i;
1258 keymap_flags = *cp1++;
1259 while ((ch = *cp1)) {
1260 if (ch >= MAX_KEY) {
1261 pr_debug("(%d), not valid key, (max_allowed = %d)\n", ch, MAX_KEY);
1262 return -EINVAL;
1264 spk_our_keys[ch] = cp1;
1265 cp1 += states + 1;
1267 return 0;
1270 static struct var_t spk_vars[] = {
1271 /* bell must be first to set high limit */
1272 {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
1273 {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
1274 {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
1275 {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
1276 {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
1277 {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1278 {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1279 {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
1280 {SAY_CONTROL, TOGGLE_0},
1281 {SAY_WORD_CTL, TOGGLE_0},
1282 {NO_INTERRUPT, TOGGLE_0},
1283 {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
1284 V_LAST_VAR
1287 static void toggle_cursoring(struct vc_data *vc)
1289 if (cursor_track == read_all_mode)
1290 cursor_track = prev_cursor_track;
1291 if (++cursor_track >= CT_Max)
1292 cursor_track = 0;
1293 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
1296 void spk_reset_default_chars(void)
1298 int i;
1300 /* First, free any non-default */
1301 for (i = 0; i < 256; i++) {
1302 if (spk_characters[i] &&
1303 (spk_characters[i] != spk_default_chars[i]))
1304 kfree(spk_characters[i]);
1307 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
1310 void spk_reset_default_chartab(void)
1312 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1315 static const struct st_bits_data *pb_edit;
1317 static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1319 short mask = pb_edit->mask, ch_type = spk_chartab[ch];
1321 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
1322 return -1;
1323 if (ch == SPACE) {
1324 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1325 spk_special_handler = NULL;
1326 return 1;
1328 if (mask < PUNC && !(ch_type & PUNC))
1329 return -1;
1330 spk_chartab[ch] ^= mask;
1331 speak_char(ch);
1332 synth_printf(" %s\n",
1333 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1334 spk_msg_get(MSG_OFF));
1335 return 1;
1338 /* Allocation concurrency is protected by the console semaphore */
1339 static int speakup_allocate(struct vc_data *vc, gfp_t gfp_flags)
1341 int vc_num;
1343 vc_num = vc->vc_num;
1344 if (!speakup_console[vc_num]) {
1345 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
1346 gfp_flags);
1347 if (!speakup_console[vc_num])
1348 return -ENOMEM;
1349 speakup_date(vc);
1350 } else if (!spk_parked) {
1351 speakup_date(vc);
1354 return 0;
1357 static void speakup_deallocate(struct vc_data *vc)
1359 int vc_num;
1361 vc_num = vc->vc_num;
1362 kfree(speakup_console[vc_num]);
1363 speakup_console[vc_num] = NULL;
1366 static u_char is_cursor;
1367 static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1368 static int cursor_con;
1370 static void reset_highlight_buffers(struct vc_data *);
1372 static int read_all_key;
1374 static int in_keyboard_notifier;
1376 static void start_read_all_timer(struct vc_data *vc, int command);
1378 enum {
1379 RA_NOTHING,
1380 RA_NEXT_SENT,
1381 RA_PREV_LINE,
1382 RA_NEXT_LINE,
1383 RA_PREV_SENT,
1384 RA_DOWN_ARROW,
1385 RA_TIMER,
1386 RA_FIND_NEXT_SENT,
1387 RA_FIND_PREV_SENT,
1390 static void kbd_fakekey2(struct vc_data *vc, int command)
1392 del_timer(&cursor_timer);
1393 speakup_fake_down_arrow();
1394 start_read_all_timer(vc, command);
1397 static void read_all_doc(struct vc_data *vc)
1399 if ((vc->vc_num != fg_console) || !synth || spk_shut_up)
1400 return;
1401 if (!synth_supports_indexing())
1402 return;
1403 if (cursor_track != read_all_mode)
1404 prev_cursor_track = cursor_track;
1405 cursor_track = read_all_mode;
1406 spk_reset_index_count(0);
1407 if (get_sentence_buf(vc, 0) == -1) {
1408 del_timer(&cursor_timer);
1409 if (!in_keyboard_notifier)
1410 speakup_fake_down_arrow();
1411 start_read_all_timer(vc, RA_DOWN_ARROW);
1412 } else {
1413 say_sentence_num(0, 0);
1414 synth_insert_next_index(0);
1415 start_read_all_timer(vc, RA_TIMER);
1419 static void stop_read_all(struct vc_data *vc)
1421 del_timer(&cursor_timer);
1422 cursor_track = prev_cursor_track;
1423 spk_shut_up &= 0xfe;
1424 spk_do_flush();
1427 static void start_read_all_timer(struct vc_data *vc, int command)
1429 struct var_t *cursor_timeout;
1431 cursor_con = vc->vc_num;
1432 read_all_key = command;
1433 cursor_timeout = spk_get_var(CURSOR_TIME);
1434 mod_timer(&cursor_timer,
1435 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1438 static void handle_cursor_read_all(struct vc_data *vc, int command)
1440 int indcount, sentcount, rv, sn;
1442 switch (command) {
1443 case RA_NEXT_SENT:
1444 /* Get Current Sentence */
1445 spk_get_index_count(&indcount, &sentcount);
1446 /*printk("%d %d ", indcount, sentcount); */
1447 spk_reset_index_count(sentcount + 1);
1448 if (indcount == 1) {
1449 if (!say_sentence_num(sentcount + 1, 0)) {
1450 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1451 return;
1453 synth_insert_next_index(0);
1454 } else {
1455 sn = 0;
1456 if (!say_sentence_num(sentcount + 1, 1)) {
1457 sn = 1;
1458 spk_reset_index_count(sn);
1459 } else {
1460 synth_insert_next_index(0);
1462 if (!say_sentence_num(sn, 0)) {
1463 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1464 return;
1466 synth_insert_next_index(0);
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);
1485 break;
1486 case RA_FIND_NEXT_SENT:
1487 rv = get_sentence_buf(vc, 0);
1488 if (rv == -1)
1489 read_all_doc(vc);
1490 if (rv == 0) {
1491 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1492 } else {
1493 say_sentence_num(1, 0);
1494 synth_insert_next_index(0);
1495 start_read_all_timer(vc, RA_TIMER);
1497 break;
1498 case RA_FIND_PREV_SENT:
1499 break;
1500 case RA_TIMER:
1501 spk_get_index_count(&indcount, &sentcount);
1502 if (indcount < 2)
1503 kbd_fakekey2(vc, RA_DOWN_ARROW);
1504 else
1505 start_read_all_timer(vc, RA_TIMER);
1506 break;
1510 static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1512 unsigned long flags;
1514 spin_lock_irqsave(&speakup_info.spinlock, flags);
1515 if (cursor_track == read_all_mode) {
1516 spk_parked &= 0xfe;
1517 if (!synth || up_flag || spk_shut_up) {
1518 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1519 return NOTIFY_STOP;
1521 del_timer(&cursor_timer);
1522 spk_shut_up &= 0xfe;
1523 spk_do_flush();
1524 start_read_all_timer(vc, value + 1);
1525 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1526 return NOTIFY_STOP;
1528 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1529 return NOTIFY_OK;
1532 static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1534 unsigned long flags;
1535 struct var_t *cursor_timeout;
1537 spin_lock_irqsave(&speakup_info.spinlock, flags);
1538 spk_parked &= 0xfe;
1539 if (!synth || up_flag || spk_shut_up || cursor_track == CT_Off) {
1540 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1541 return;
1543 spk_shut_up &= 0xfe;
1544 if (spk_no_intr)
1545 spk_do_flush();
1546 /* the key press flushes if !no_inter but we want to flush on cursor
1547 * moves regardless of no_inter state
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);
1557 cursor_timeout = spk_get_var(CURSOR_TIME);
1558 mod_timer(&cursor_timer,
1559 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
1560 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1563 static void update_color_buffer(struct vc_data *vc, const u16 *ic, int len)
1565 int i, bi, hi;
1566 int vc_num = vc->vc_num;
1568 bi = (vc->vc_attr & 0x70) >> 4;
1569 hi = speakup_console[vc_num]->ht.highsize[bi];
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;
1577 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1578 if (ic[i] > 32) {
1579 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1580 hi++;
1581 } else if ((ic[i] == 32) && (hi != 0)) {
1582 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1583 32) {
1584 speakup_console[vc_num]->ht.highbuf[bi][hi] =
1585 ic[i];
1586 hi++;
1589 i++;
1591 speakup_console[vc_num]->ht.highsize[bi] = hi;
1594 static void reset_highlight_buffers(struct vc_data *vc)
1596 int i;
1597 int vc_num = vc->vc_num;
1599 for (i = 0; i < 8; i++)
1600 speakup_console[vc_num]->ht.highsize[i] = 0;
1603 static int count_highlight_color(struct vc_data *vc)
1605 int i, bg;
1606 int cc;
1607 int vc_num = vc->vc_num;
1608 u16 ch;
1609 u16 *start = (u16 *)vc->vc_origin;
1611 for (i = 0; i < 8; i++)
1612 speakup_console[vc_num]->ht.bgcount[i] = 0;
1614 for (i = 0; i < vc->vc_rows; i++) {
1615 u16 *end = start + vc->vc_cols * 2;
1616 u16 *ptr;
1618 for (ptr = start; ptr < end; ptr++) {
1619 ch = get_attributes(vc, ptr);
1620 bg = (ch & 0x70) >> 4;
1621 speakup_console[vc_num]->ht.bgcount[bg]++;
1623 start += vc->vc_size_row;
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;
1633 static int get_highlight_color(struct vc_data *vc)
1635 int i, j;
1636 unsigned int cptr[8];
1637 int vc_num = vc->vc_num;
1639 for (i = 0; i < 8; i++)
1640 cptr[i] = i;
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]] >
1645 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1646 swap(cptr[i], cptr[j]);
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;
1655 static int speak_highlight(struct vc_data *vc)
1657 int hc, d;
1658 int vc_num = vc->vc_num;
1660 if (count_highlight_color(vc) == 1)
1661 return 0;
1662 hc = get_highlight_color(vc);
1663 if (hc != -1) {
1664 d = vc->vc_y - speakup_console[vc_num]->ht.cy;
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;
1669 spk_do_flush();
1670 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
1671 speakup_console[vc_num]->ht.highsize[hc]);
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;
1677 return 0;
1680 static void cursor_done(struct timer_list *unused)
1682 struct vc_data *vc = vc_cons[cursor_con].d;
1683 unsigned long flags;
1685 del_timer(&cursor_timer);
1686 spin_lock_irqsave(&speakup_info.spinlock, flags);
1687 if (cursor_con != fg_console) {
1688 is_cursor = 0;
1689 goto out;
1691 speakup_date(vc);
1692 if (win_enabled) {
1693 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
1694 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
1695 spk_keydown = 0;
1696 is_cursor = 0;
1697 goto out;
1700 if (cursor_track == read_all_mode) {
1701 handle_cursor_read_all(vc, read_all_key);
1702 goto out;
1704 if (cursor_track == CT_Highlight) {
1705 if (speak_highlight(vc)) {
1706 spk_keydown = 0;
1707 is_cursor = 0;
1708 goto out;
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);
1717 spk_keydown = 0;
1718 is_cursor = 0;
1719 out:
1720 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1723 /* called by: vt_notifier_call() */
1724 static void speakup_bs(struct vc_data *vc)
1726 unsigned long flags;
1728 if (!speakup_console[vc->vc_num])
1729 return;
1730 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1731 /* Speakup output, discard */
1732 return;
1733 if (!spk_parked)
1734 speakup_date(vc);
1735 if (spk_shut_up || !synth) {
1736 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1737 return;
1739 if (vc->vc_num == fg_console && spk_keydown) {
1740 spk_keydown = 0;
1741 if (!is_cursor)
1742 say_char(vc);
1744 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1747 /* called by: vt_notifier_call() */
1748 static void speakup_con_write(struct vc_data *vc, u16 *str, int len)
1750 unsigned long flags;
1752 if ((vc->vc_num != fg_console) || spk_shut_up || !synth)
1753 return;
1754 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1755 /* Speakup output, discard */
1756 return;
1757 if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1))
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);
1762 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1763 return;
1765 if (win_enabled) {
1766 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
1767 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
1768 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1769 return;
1773 spkup_write(str, len);
1774 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1777 static void speakup_con_update(struct vc_data *vc)
1779 unsigned long flags;
1781 if (!speakup_console[vc->vc_num] || spk_parked)
1782 return;
1783 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
1784 /* Speakup output, discard */
1785 return;
1786 speakup_date(vc);
1787 if (vc->vc_mode == KD_GRAPHICS && !spk_paused && spk_str_pause[0]) {
1788 synth_printf("%s", spk_str_pause);
1789 spk_paused = true;
1791 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1794 static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1796 unsigned long flags;
1797 int on_off = 2;
1798 char *label;
1800 if (!synth || up_flag || spk_killed)
1801 return;
1802 spin_lock_irqsave(&speakup_info.spinlock, flags);
1803 spk_shut_up &= 0xfe;
1804 if (spk_no_intr)
1805 spk_do_flush();
1806 switch (value) {
1807 case KVAL(K_CAPS):
1808 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
1809 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
1810 break;
1811 case KVAL(K_NUM):
1812 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
1813 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
1814 break;
1815 case KVAL(K_HOLD):
1816 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
1817 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
1818 if (speakup_console[vc->vc_num])
1819 speakup_console[vc->vc_num]->tty_stopped = on_off;
1820 break;
1821 default:
1822 spk_parked &= 0xfe;
1823 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1824 return;
1826 if (on_off < 2)
1827 synth_printf("%s %s\n",
1828 label, spk_msg_get(MSG_STATUS_START + on_off));
1829 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
1832 static int inc_dec_var(u_char value)
1834 struct st_var_header *p_header;
1835 struct var_t *var_data;
1836 char num_buf[32];
1837 char *cp = num_buf;
1838 char *pn;
1839 int var_id = (int)value - VAR_START;
1840 int how = (var_id & 1) ? E_INC : E_DEC;
1842 var_id = var_id / 2 + FIRST_SET_VAR;
1843 p_header = spk_get_var_header(var_id);
1844 if (!p_header)
1845 return -1;
1846 if (p_header->var_type != VAR_NUM)
1847 return -1;
1848 var_data = p_header->data;
1849 if (spk_set_num_var(1, p_header, how) != 0)
1850 return -1;
1851 if (!spk_close_press) {
1852 for (pn = p_header->name; *pn; pn++) {
1853 if (*pn == '_')
1854 *cp = SPACE;
1855 else
1856 *cp++ = *pn;
1859 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
1860 var_data->u.n.value);
1861 synth_printf("%s", num_buf);
1862 return 0;
1865 static void speakup_win_set(struct vc_data *vc)
1867 char info[40];
1869 if (win_start > 1) {
1870 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
1871 return;
1873 if (spk_x < win_left || spk_y < win_top) {
1874 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
1875 return;
1877 if (win_start && spk_x == win_left && spk_y == win_top) {
1878 win_left = 0;
1879 win_right = vc->vc_cols - 1;
1880 win_bottom = spk_y;
1881 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
1882 (int)win_top + 1);
1883 } else {
1884 if (!win_start) {
1885 win_top = spk_y;
1886 win_left = spk_x;
1887 } else {
1888 win_bottom = spk_y;
1889 win_right = spk_x;
1891 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
1892 (win_start) ?
1893 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
1894 (int)spk_y + 1, (int)spk_x + 1);
1896 synth_printf("%s\n", info);
1897 win_start++;
1900 static void speakup_win_clear(struct vc_data *vc)
1902 win_top = 0;
1903 win_bottom = 0;
1904 win_left = 0;
1905 win_right = 0;
1906 win_start = 0;
1907 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
1910 static void speakup_win_enable(struct vc_data *vc)
1912 if (win_start < 2) {
1913 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
1914 return;
1916 win_enabled ^= 1;
1917 if (win_enabled)
1918 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
1919 else
1920 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
1923 static void speakup_bits(struct vc_data *vc)
1925 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
1927 if (spk_special_handler || val < 1 || val > 6) {
1928 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
1929 return;
1931 pb_edit = &spk_punc_info[val];
1932 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1933 spk_special_handler = edit_bits;
1936 static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1938 static u_char goto_buf[8];
1939 static int num;
1940 int maxlen;
1941 char *cp;
1942 u16 wch;
1944 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1945 goto do_goto;
1946 if (type == KT_LATIN && ch == '\n')
1947 goto do_goto;
1948 if (type != 0)
1949 goto oops;
1950 if (ch == 8) {
1951 u16 wch;
1953 if (num == 0)
1954 return -1;
1955 wch = goto_buf[--num];
1956 goto_buf[num] = '\0';
1957 spkup_write(&wch, 1);
1958 return 1;
1960 if (ch < '+' || ch > 'y')
1961 goto oops;
1962 wch = ch;
1963 goto_buf[num++] = ch;
1964 goto_buf[num] = '\0';
1965 spkup_write(&wch, 1);
1966 maxlen = (*goto_buf >= '0') ? 3 : 4;
1967 if ((ch == '+' || ch == '-') && num == 1)
1968 return 1;
1969 if (ch >= '0' && ch <= '9' && num < maxlen)
1970 return 1;
1971 if (num < maxlen - 1 || num > maxlen)
1972 goto oops;
1973 if (ch < 'x' || ch > 'y') {
1974 oops:
1975 if (!spk_killed)
1976 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
1977 goto_buf[num = 0] = '\0';
1978 spk_special_handler = NULL;
1979 return 1;
1982 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1984 if (*cp == 'x') {
1985 if (*goto_buf < '0')
1986 goto_pos += spk_x;
1987 else if (goto_pos > 0)
1988 goto_pos--;
1990 if (goto_pos >= vc->vc_cols)
1991 goto_pos = vc->vc_cols - 1;
1992 goto_x = 1;
1993 } else {
1994 if (*goto_buf < '0')
1995 goto_pos += spk_y;
1996 else if (goto_pos > 0)
1997 goto_pos--;
1999 if (goto_pos >= vc->vc_rows)
2000 goto_pos = vc->vc_rows - 1;
2001 goto_x = 0;
2003 goto_buf[num = 0] = '\0';
2004 do_goto:
2005 spk_special_handler = NULL;
2006 spk_parked |= 0x01;
2007 if (goto_x) {
2008 spk_pos -= spk_x * 2;
2009 spk_x = goto_pos;
2010 spk_pos += goto_pos * 2;
2011 say_word(vc);
2012 } else {
2013 spk_y = goto_pos;
2014 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
2015 say_line(vc);
2017 return 1;
2020 static void speakup_goto(struct vc_data *vc)
2022 if (spk_special_handler) {
2023 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
2024 return;
2026 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
2027 spk_special_handler = handle_goto;
2030 static void speakup_help(struct vc_data *vc)
2032 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
2035 static void do_nothing(struct vc_data *vc)
2037 return; /* flush done in do_spkup */
2040 static u_char key_speakup, spk_key_locked;
2042 static void speakup_lock(struct vc_data *vc)
2044 if (!spk_key_locked) {
2045 spk_key_locked = 16;
2046 key_speakup = 16;
2047 } else {
2048 spk_key_locked = 0;
2049 key_speakup = 0;
2053 typedef void (*spkup_hand) (struct vc_data *);
2054 static spkup_hand spkup_handler[] = {
2055 /* must be ordered same as defines in speakup.h */
2056 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2057 speakup_cut, speakup_paste, say_first_char, say_last_char,
2058 say_char, say_prev_char, say_next_char,
2059 say_word, say_prev_word, say_next_word,
2060 say_line, say_prev_line, say_next_line,
2061 top_edge, bottom_edge, left_edge, right_edge,
2062 spell_word, spell_word, say_screen,
2063 say_position, say_attributes,
2064 speakup_off, speakup_parked, say_line, /* this is for indent */
2065 say_from_top, say_to_bottom,
2066 say_from_left, say_to_right,
2067 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2068 speakup_bits, speakup_bits, speakup_bits,
2069 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2070 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2073 static void do_spkup(struct vc_data *vc, u_char value)
2075 if (spk_killed && value != SPEECH_KILL)
2076 return;
2077 spk_keydown = 0;
2078 spk_lastkey = 0;
2079 spk_shut_up &= 0xfe;
2080 this_speakup_key = value;
2081 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
2082 spk_do_flush();
2083 (*spkup_handler[value]) (vc);
2084 } else {
2085 if (inc_dec_var(value) < 0)
2086 bleep(9);
2090 static const char *pad_chars = "0123456789+-*/\015,.?()";
2092 static int
2093 speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
2094 int up_flag)
2096 unsigned long flags;
2097 int kh;
2098 u_char *key_info;
2099 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2100 u_char shift_info, offset;
2101 int ret = 0;
2103 if (!synth)
2104 return 0;
2106 spin_lock_irqsave(&speakup_info.spinlock, flags);
2107 tty = vc->port.tty;
2108 if (type >= 0xf0)
2109 type -= 0xf0;
2110 if (type == KT_PAD &&
2111 (vt_get_leds(fg_console, VC_NUMLOCK))) {
2112 if (up_flag) {
2113 spk_keydown = 0;
2114 goto out;
2116 value = spk_lastkey = pad_chars[value];
2117 spk_keydown++;
2118 spk_parked &= 0xfe;
2119 goto no_map;
2121 if (keycode >= MAX_KEY)
2122 goto no_map;
2123 key_info = spk_our_keys[keycode];
2124 if (!key_info)
2125 goto no_map;
2126 /* Check valid read all mode keys */
2127 if ((cursor_track == read_all_mode) && (!up_flag)) {
2128 switch (value) {
2129 case KVAL(K_DOWN):
2130 case KVAL(K_UP):
2131 case KVAL(K_LEFT):
2132 case KVAL(K_RIGHT):
2133 case KVAL(K_PGUP):
2134 case KVAL(K_PGDN):
2135 break;
2136 default:
2137 stop_read_all(vc);
2138 break;
2141 shift_info = (shift_state & 0x0f) + key_speakup;
2142 offset = spk_shift_table[shift_info];
2143 if (offset) {
2144 new_key = key_info[offset];
2145 if (new_key) {
2146 ret = 1;
2147 if (new_key == SPK_KEY) {
2148 if (!spk_key_locked)
2149 key_speakup = (up_flag) ? 0 : 16;
2150 if (up_flag || spk_killed)
2151 goto out;
2152 spk_shut_up &= 0xfe;
2153 spk_do_flush();
2154 goto out;
2156 if (up_flag)
2157 goto out;
2158 if (last_keycode == keycode &&
2159 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
2160 spk_close_press = 1;
2161 offset = spk_shift_table[shift_info + 32];
2162 /* double press? */
2163 if (offset && key_info[offset])
2164 new_key = key_info[offset];
2166 last_keycode = keycode;
2167 last_spk_jiffy = jiffies;
2168 type = KT_SPKUP;
2169 value = new_key;
2172 no_map:
2173 if (type == KT_SPKUP && !spk_special_handler) {
2174 do_spkup(vc, new_key);
2175 spk_close_press = 0;
2176 ret = 1;
2177 goto out;
2179 if (up_flag || spk_killed || type == KT_SHIFT)
2180 goto out;
2181 spk_shut_up &= 0xfe;
2182 kh = (value == KVAL(K_DOWN)) ||
2183 (value == KVAL(K_UP)) ||
2184 (value == KVAL(K_LEFT)) ||
2185 (value == KVAL(K_RIGHT));
2186 if ((cursor_track != read_all_mode) || !kh)
2187 if (!spk_no_intr)
2188 spk_do_flush();
2189 if (spk_special_handler) {
2190 if (type == KT_SPEC && value == 1) {
2191 value = '\n';
2192 type = KT_LATIN;
2193 } else if (type == KT_LETTER) {
2194 type = KT_LATIN;
2195 } else if (value == 0x7f) {
2196 value = 8; /* make del = backspace */
2198 ret = (*spk_special_handler) (vc, type, value, keycode);
2199 spk_close_press = 0;
2200 if (ret < 0)
2201 bleep(9);
2202 goto out;
2204 last_keycode = 0;
2205 out:
2206 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
2207 return ret;
2210 static int keyboard_notifier_call(struct notifier_block *nb,
2211 unsigned long code, void *_param)
2213 struct keyboard_notifier_param *param = _param;
2214 struct vc_data *vc = param->vc;
2215 int up = !param->down;
2216 int ret = NOTIFY_OK;
2217 static int keycode; /* to hold the current keycode */
2219 in_keyboard_notifier = 1;
2221 if (vc->vc_mode == KD_GRAPHICS)
2222 goto out;
2225 * First, determine whether we are handling a fake keypress on
2226 * the current processor. If we are, then return NOTIFY_OK,
2227 * to pass the keystroke up the chain. This prevents us from
2228 * trying to take the Speakup lock while it is held by the
2229 * processor on which the simulated keystroke was generated.
2230 * Also, the simulated keystrokes should be ignored by Speakup.
2233 if (speakup_fake_key_pressed())
2234 goto out;
2236 switch (code) {
2237 case KBD_KEYCODE:
2238 /* speakup requires keycode and keysym currently */
2239 keycode = param->value;
2240 break;
2241 case KBD_UNBOUND_KEYCODE:
2242 /* not used yet */
2243 break;
2244 case KBD_UNICODE:
2245 /* not used yet */
2246 break;
2247 case KBD_KEYSYM:
2248 if (speakup_key(vc, param->shift, keycode, param->value, up))
2249 ret = NOTIFY_STOP;
2250 else if (KTYP(param->value) == KT_CUR)
2251 ret = pre_handle_cursor(vc, KVAL(param->value), up);
2252 break;
2253 case KBD_POST_KEYSYM:{
2254 unsigned char type = KTYP(param->value) - 0xf0;
2255 unsigned char val = KVAL(param->value);
2257 switch (type) {
2258 case KT_SHIFT:
2259 do_handle_shift(vc, val, up);
2260 break;
2261 case KT_LATIN:
2262 case KT_LETTER:
2263 do_handle_latin(vc, val, up);
2264 break;
2265 case KT_CUR:
2266 do_handle_cursor(vc, val, up);
2267 break;
2268 case KT_SPEC:
2269 do_handle_spec(vc, val, up);
2270 break;
2272 break;
2275 out:
2276 in_keyboard_notifier = 0;
2277 return ret;
2280 static int vt_notifier_call(struct notifier_block *nb,
2281 unsigned long code, void *_param)
2283 struct vt_notifier_param *param = _param;
2284 struct vc_data *vc = param->vc;
2286 switch (code) {
2287 case VT_ALLOCATE:
2288 if (vc->vc_mode == KD_TEXT)
2289 speakup_allocate(vc, GFP_ATOMIC);
2290 break;
2291 case VT_DEALLOCATE:
2292 speakup_deallocate(vc);
2293 break;
2294 case VT_WRITE:
2295 if (param->c == '\b') {
2296 speakup_bs(vc);
2297 } else {
2298 u16 d = param->c;
2300 speakup_con_write(vc, &d, 1);
2302 break;
2303 case VT_UPDATE:
2304 speakup_con_update(vc);
2305 break;
2307 return NOTIFY_OK;
2310 /* called by: module_exit() */
2311 static void __exit speakup_exit(void)
2313 int i;
2315 unregister_keyboard_notifier(&keyboard_notifier_block);
2316 unregister_vt_notifier(&vt_notifier_block);
2317 speakup_unregister_devsynth();
2318 speakup_cancel_paste();
2319 del_timer_sync(&cursor_timer);
2320 kthread_stop(speakup_task);
2321 speakup_task = NULL;
2322 mutex_lock(&spk_mutex);
2323 synth_release();
2324 mutex_unlock(&spk_mutex);
2325 spk_ttyio_unregister_ldisc();
2327 speakup_kobj_exit();
2329 for (i = 0; i < MAX_NR_CONSOLES; i++)
2330 kfree(speakup_console[i]);
2332 speakup_remove_virtual_keyboard();
2334 for (i = 0; i < MAXVARS; i++)
2335 speakup_unregister_var(i);
2337 for (i = 0; i < 256; i++) {
2338 if (spk_characters[i] != spk_default_chars[i])
2339 kfree(spk_characters[i]);
2342 spk_free_user_msgs();
2345 /* call by: module_init() */
2346 static int __init speakup_init(void)
2348 int i;
2349 long err = 0;
2350 struct vc_data *vc = vc_cons[fg_console].d;
2351 struct var_t *var;
2353 /* These first few initializations cannot fail. */
2354 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2355 spk_reset_default_chars();
2356 spk_reset_default_chartab();
2357 spk_strlwr(synth_name);
2358 spk_vars[0].u.n.high = vc->vc_cols;
2359 for (var = spk_vars; var->var_id != MAXVARS; var++)
2360 speakup_register_var(var);
2361 for (var = synth_time_vars;
2362 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
2363 speakup_register_var(var);
2364 for (i = 1; spk_punc_info[i].mask != 0; i++)
2365 spk_set_mask_bits(NULL, i, 2);
2367 spk_set_key_info(spk_key_defaults, spk_key_buf);
2369 /* From here on out, initializations can fail. */
2370 err = speakup_add_virtual_keyboard();
2371 if (err)
2372 goto error_virtkeyboard;
2374 for (i = 0; i < MAX_NR_CONSOLES; i++)
2375 if (vc_cons[i].d) {
2376 err = speakup_allocate(vc_cons[i].d, GFP_KERNEL);
2377 if (err)
2378 goto error_kobjects;
2381 if (spk_quiet_boot)
2382 spk_shut_up |= 0x01;
2384 err = speakup_kobj_init();
2385 if (err)
2386 goto error_kobjects;
2388 spk_ttyio_register_ldisc();
2389 synth_init(synth_name);
2390 speakup_register_devsynth();
2392 * register_devsynth might fail, but this error is not fatal.
2393 * /dev/synth is an extra feature; the rest of Speakup
2394 * will work fine without it.
2397 err = register_keyboard_notifier(&keyboard_notifier_block);
2398 if (err)
2399 goto error_kbdnotifier;
2400 err = register_vt_notifier(&vt_notifier_block);
2401 if (err)
2402 goto error_vtnotifier;
2404 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
2406 if (IS_ERR(speakup_task)) {
2407 err = PTR_ERR(speakup_task);
2408 goto error_task;
2411 set_user_nice(speakup_task, 10);
2412 wake_up_process(speakup_task);
2414 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2415 pr_info("synth name on entry is: %s\n", synth_name);
2416 goto out;
2418 error_task:
2419 unregister_vt_notifier(&vt_notifier_block);
2421 error_vtnotifier:
2422 unregister_keyboard_notifier(&keyboard_notifier_block);
2423 del_timer(&cursor_timer);
2425 error_kbdnotifier:
2426 speakup_unregister_devsynth();
2427 mutex_lock(&spk_mutex);
2428 synth_release();
2429 mutex_unlock(&spk_mutex);
2430 speakup_kobj_exit();
2432 error_kobjects:
2433 for (i = 0; i < MAX_NR_CONSOLES; i++)
2434 kfree(speakup_console[i]);
2436 speakup_remove_virtual_keyboard();
2438 error_virtkeyboard:
2439 for (i = 0; i < MAXVARS; i++)
2440 speakup_unregister_var(i);
2442 for (i = 0; i < 256; i++) {
2443 if (spk_characters[i] != spk_default_chars[i])
2444 kfree(spk_characters[i]);
2447 spk_free_user_msgs();
2449 out:
2450 return err;
2453 module_init(speakup_init);
2454 module_exit(speakup_exit);