2 * review functions for the speakup screen review package.
3 * originally written by: Kirk Reiser and Andy Berdan.
5 * extensively modified by David Borowski.
7 ** Copyright (C) 1998 Kirk Reiser.
8 * Copyright (C) 2003 David Borowski.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <linux/kernel.h>
27 #include <linux/tty.h>
28 #include <linux/mm.h> /* __get_free_page() and friends */
29 #include <linux/vt_kern.h>
30 #include <linux/ctype.h>
31 #include <linux/selection.h>
32 #include <linux/unistd.h>
33 #include <linux/jiffies.h>
34 #include <linux/kthread.h>
35 #include <linux/keyboard.h> /* for KT_SHIFT */
36 #include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
37 #include <linux/input.h>
38 #include <linux/kmod.h>
40 /* speakup_*_selection */
41 #include <linux/module.h>
42 #include <linux/sched.h>
43 #include <linux/slab.h>
44 #include <linux/types.h>
45 #include <linux/consolemap.h>
47 #include <linux/spinlock.h>
48 #include <linux/notifier.h>
50 #include <linux/uaccess.h> /* copy_from|to|user() and others */
55 #define MAX_DELAY msecs_to_jiffies(500)
56 #define MINECHOCHAR SPACE
58 MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
59 MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
60 MODULE_DESCRIPTION("Speakup console speech");
61 MODULE_LICENSE("GPL");
62 MODULE_VERSION(SPEAKUP_VERSION
);
65 module_param_named(synth
, synth_name
, charp
, S_IRUGO
);
66 module_param_named(quiet
, spk_quiet_boot
, bool, S_IRUGO
);
68 MODULE_PARM_DESC(synth
, "Synth to start if speakup is built in.");
69 MODULE_PARM_DESC(quiet
, "Do not announce when the synthesizer is found.");
71 special_func spk_special_handler
;
73 short spk_pitch_shift
, synth_flags
;
75 int spk_attrib_bleep
, spk_bleeps
, spk_bleep_time
= 10;
76 int spk_no_intr
, spk_spell_delay
;
77 int spk_key_echo
, spk_say_word_ctl
;
78 int spk_say_ctrl
, spk_bell_pos
;
80 int spk_punc_level
, spk_reading_punc
;
81 char spk_str_caps_start
[MAXVARLEN
+ 1] = "\0";
82 char spk_str_caps_stop
[MAXVARLEN
+ 1] = "\0";
83 const struct st_bits_data spk_punc_info
[] = {
85 {"some", "/$%&@", SOME
},
86 {"most", "$%&#()=+*/@^<>|\\", MOST
},
87 {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC
},
88 {"delimiters", "", B_WDLM
},
89 {"repeats", "()", CH_RPT
},
90 {"extended numeric", "", B_EXNUM
},
91 {"symbols", "", B_SYM
},
95 static char mark_cut_flag
;
97 static u_char
*spk_shift_table
;
98 u_char
*spk_our_keys
[MAX_KEY
];
99 u_char spk_key_buf
[600];
100 const u_char spk_key_defaults
[] = {
101 #include "speakupmap.h"
104 /* Speakup Cursor Track Variables */
105 static int cursor_track
= 1, prev_cursor_track
= 1;
107 /* cursor track modes, must be ordered same as cursor_msgs */
115 #define read_all_mode CT_Max
117 static struct tty_struct
*tty
;
119 static void spkup_write(const char *in_buf
, int count
);
121 static char *phonetic
[] = {
122 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
123 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
125 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
126 "x ray", "yankee", "zulu"
129 /* array of 256 char pointers (one for each character description)
130 * initialized to default_chars and user selectable via
131 * /proc/speakup/characters
133 char *spk_characters
[256];
135 char *spk_default_chars
[256] = {
136 /*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
137 /*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
138 /*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
139 /*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
141 /*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
143 /*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
146 /*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
148 /*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
149 /*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
150 /*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
151 /*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
152 /*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
155 /*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
156 /*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
157 /*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
158 /*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
159 /*127*/ "del", "control", "control", "control", "control", "control",
160 "control", "control", "control", "control", "control",
161 /*138*/ "control", "control", "control", "control", "control",
162 "control", "control", "control", "control", "control",
163 "control", "control",
164 /*150*/ "control", "control", "control", "control", "control",
165 "control", "control", "control", "control", "control",
166 /*160*/ "nbsp", "inverted bang",
167 /*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
168 /*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
169 /*172*/ "not", "soft hyphen", "registered", "macron",
170 /*176*/ "degrees", "plus or minus", "super two", "super three",
171 /*180*/ "acute accent", "micro", "pilcrow", "middle dot",
172 /*184*/ "cedilla", "super one", "male ordinal", "double right angle",
173 /*188*/ "one quarter", "one half", "three quarters",
175 /*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
177 /*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
179 /*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
181 /*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
182 /*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
184 /*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
185 /*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
186 /*230*/ "ae", "c cidella", "e grave", "e acute",
187 /*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
189 /*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
191 /*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
193 /* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
196 /* array of 256 u_short (one for each character)
197 * initialized to default_chartab and user selectable via
198 * /sys/module/speakup/parameters/chartab
200 u_short spk_chartab
[256];
202 static u_short default_chartab
[256] = {
203 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 0-7 */
204 B_CTL
, B_CTL
, A_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 8-15 */
205 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /*16-23 */
206 B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, B_CTL
, /* 24-31 */
207 WDLM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* !"#$%&' */
208 PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, A_PUNC
, A_PUNC
, PUNC
, /* ()*+, -./ */
209 NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, NUM
, /* 01234567 */
210 NUM
, NUM
, A_PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, A_PUNC
, /* 89:;<=>? */
211 PUNC
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* @ABCDEFG */
212 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* HIJKLMNO */
213 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* PQRSTUVW */
214 A_CAP
, A_CAP
, A_CAP
, PUNC
, PUNC
, PUNC
, PUNC
, PUNC
, /* XYZ[\]^_ */
215 PUNC
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* `abcdefg */
216 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* hijklmno */
217 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* pqrstuvw */
218 ALPHA
, ALPHA
, ALPHA
, PUNC
, PUNC
, PUNC
, PUNC
, 0, /* xyz{|}~ */
219 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 128-134 */
221 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 136-142 */
223 B_CAPSYM
, B_CAPSYM
, B_SYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /* 144-150 */
225 B_SYM
, B_SYM
, B_CAPSYM
, B_CAPSYM
, B_SYM
, B_SYM
, B_SYM
, /*152-158 */
227 WDLM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_CAPSYM
, /* 160-166 */
229 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 168-175 */
230 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 176-183 */
231 B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, B_SYM
, /* 184-191 */
232 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 192-199 */
233 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, /* 200-207 */
234 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, B_SYM
, /* 208-215 */
235 A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, A_CAP
, ALPHA
, /* 216-223 */
236 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 224-231 */
237 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, /* 232-239 */
238 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, B_SYM
, /* 240-247 */
239 ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
, ALPHA
/* 248-255 */
242 struct task_struct
*speakup_task
;
243 struct bleep spk_unprocessed_sound
;
244 static int spk_keydown
;
245 static u_char spk_lastkey
, spk_close_press
, keymap_flags
;
246 static u_char last_keycode
, this_speakup_key
;
247 static u_long last_spk_jiffy
;
249 struct st_spk_t
*speakup_console
[MAX_NR_CONSOLES
];
251 DEFINE_MUTEX(spk_mutex
);
253 static int keyboard_notifier_call(struct notifier_block
*,
254 unsigned long code
, void *param
);
256 static struct notifier_block keyboard_notifier_block
= {
257 .notifier_call
= keyboard_notifier_call
,
260 static int vt_notifier_call(struct notifier_block
*,
261 unsigned long code
, void *param
);
263 static struct notifier_block vt_notifier_block
= {
264 .notifier_call
= vt_notifier_call
,
267 static unsigned char get_attributes(struct vc_data
*vc
, u16
*pos
)
269 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
270 return (u_char
) (scr_readw(pos
) >> 8);
273 static void speakup_date(struct vc_data
*vc
)
275 spk_x
= spk_cx
= vc
->vc_x
;
276 spk_y
= spk_cy
= vc
->vc_y
;
277 spk_pos
= spk_cp
= vc
->vc_pos
;
278 spk_old_attr
= spk_attr
;
279 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
282 static void bleep(u_short val
)
284 static const short vals
[] = {
285 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
288 int time
= spk_bleep_time
;
290 freq
= vals
[val
% 12];
292 freq
*= (1 << (val
/ 12));
293 spk_unprocessed_sound
.freq
= freq
;
294 spk_unprocessed_sound
.jiffies
= msecs_to_jiffies(time
);
295 spk_unprocessed_sound
.active
= 1;
296 /* We can only have 1 active sound at a time. */
299 static void speakup_shut_up(struct vc_data
*vc
)
310 static void speech_kill(struct vc_data
*vc
)
312 char val
= synth
->is_alive(synth
);
317 /* re-enables synth, if disabled */
318 if (val
== 2 || spk_killed
) {
320 spk_shut_up
&= ~0x40;
321 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE
));
323 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP
));
328 static void speakup_off(struct vc_data
*vc
)
330 if (spk_shut_up
& 0x80) {
332 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER
));
335 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF
));
340 static void speakup_parked(struct vc_data
*vc
)
342 if (spk_parked
& 0x80) {
344 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED
));
347 synth_printf("%s\n", spk_msg_get(MSG_PARKED
));
351 static void speakup_cut(struct vc_data
*vc
)
353 static const char err_buf
[] = "set selection failed";
356 if (!mark_cut_flag
) {
358 spk_xs
= (u_short
) spk_x
;
359 spk_ys
= (u_short
) spk_y
;
361 synth_printf("%s\n", spk_msg_get(MSG_MARK
));
364 spk_xe
= (u_short
) spk_x
;
365 spk_ye
= (u_short
) spk_y
;
367 synth_printf("%s\n", spk_msg_get(MSG_CUT
));
369 speakup_clear_selection();
370 ret
= speakup_set_selection(tty
);
374 break; /* no error */
376 pr_warn("%sEFAULT\n", err_buf
);
379 pr_warn("%sEINVAL\n", err_buf
);
382 pr_warn("%sENOMEM\n", err_buf
);
387 static void speakup_paste(struct vc_data
*vc
)
391 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED
));
393 synth_printf("%s\n", spk_msg_get(MSG_PASTE
));
394 speakup_paste_selection(tty
);
398 static void say_attributes(struct vc_data
*vc
)
400 int fg
= spk_attr
& 0x0f;
401 int bg
= spk_attr
>> 4;
404 synth_printf("%s ", spk_msg_get(MSG_BRIGHT
));
407 synth_printf("%s", spk_msg_get(MSG_COLORS_START
+ fg
));
409 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING
));
412 synth_printf(" %s ", spk_msg_get(MSG_ON
));
413 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START
+ bg
));
424 static void announce_edge(struct vc_data
*vc
, int msg_id
)
428 if ((spk_bleeps
& 2) && (msg_id
< edge_quiet
))
430 spk_msg_get(MSG_EDGE_MSGS_START
+ msg_id
- 1));
433 static void speak_char(u_char ch
)
435 char *cp
= spk_characters
[ch
];
436 struct var_t
*direct
= spk_get_var(DIRECT
);
438 if (direct
&& direct
->u
.n
.value
) {
439 if (IS_CHAR(ch
, B_CAP
)) {
441 synth_printf("%s", spk_str_caps_start
);
443 synth_printf("%c", ch
);
444 if (IS_CHAR(ch
, B_CAP
))
445 synth_printf("%s", spk_str_caps_stop
);
449 pr_info("speak_char: cp == NULL!\n");
452 synth_buffer_add(SPACE
);
453 if (IS_CHAR(ch
, B_CAP
)) {
455 synth_printf("%s", spk_str_caps_start
);
456 synth_printf("%s", cp
);
457 synth_printf("%s", spk_str_caps_stop
);
460 synth_printf("%s", spk_msg_get(MSG_CTRL
));
463 synth_printf("%s", cp
);
465 synth_buffer_add(SPACE
);
468 static u16
get_char(struct vc_data
*vc
, u16
*pos
, u_char
*attribs
)
476 pos
= screen_pos(vc
, pos
- (u16
*)vc
->vc_origin
, 1);
480 if (w
& vc
->vc_hi_font_mask
)
483 ch
= inverse_translate(vc
, c
, 0);
484 *attribs
= (w
& 0xff00) >> 8;
489 static void say_char(struct vc_data
*vc
)
493 spk_old_attr
= spk_attr
;
494 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
495 if (spk_attr
!= spk_old_attr
) {
496 if (spk_attrib_bleep
& 1)
498 if (spk_attrib_bleep
& 2)
501 speak_char(ch
& 0xff);
504 static void say_phonetic_char(struct vc_data
*vc
)
508 spk_old_attr
= spk_attr
;
509 ch
= get_char(vc
, (u_short
*) spk_pos
, &spk_attr
);
510 if (isascii(ch
) && isalpha(ch
)) {
512 synth_printf("%s\n", phonetic
[--ch
]);
514 if (IS_CHAR(ch
, B_NUM
))
515 synth_printf("%s ", spk_msg_get(MSG_NUMBER
));
520 static void say_prev_char(struct vc_data
*vc
)
524 announce_edge(vc
, edge_left
);
532 static void say_next_char(struct vc_data
*vc
)
535 if (spk_x
== vc
->vc_cols
- 1) {
536 announce_edge(vc
, edge_right
);
544 /* get_word - will first check to see if the character under the
545 * reading cursor is a space and if spk_say_word_ctl is true it will
546 * return the word space. If spk_say_word_ctl is not set it will check to
547 * see if there is a word starting on the next position to the right
548 * and return that word if it exists. If it does not exist it will
549 * move left to the beginning of any previous word on the line or the
550 * beginning off the line whichever comes first..
553 static u_long
get_word(struct vc_data
*vc
)
555 u_long cnt
= 0, tmpx
= spk_x
, tmp_pos
= spk_pos
;
560 spk_old_attr
= spk_attr
;
561 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
563 /* decided to take out the sayword if on a space (mis-information */
564 if (spk_say_word_ctl
&& ch
== SPACE
) {
566 synth_printf("%s\n", spk_msg_get(MSG_SPACE
));
568 } else if ((tmpx
< vc
->vc_cols
- 2)
569 && (ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
570 && ((char)get_char(vc
, (u_short
*) &tmp_pos
+ 1, &temp
) >
576 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
- 1, &temp
);
577 if ((ch
== SPACE
|| ch
== 0 || IS_WDLM(ch
))
578 && ((char)get_char(vc
, (u_short
*) tmp_pos
, &temp
) >
584 attr_ch
= get_char(vc
, (u_short
*) tmp_pos
, &spk_attr
);
585 buf
[cnt
++] = attr_ch
& 0xff;
586 while (tmpx
< vc
->vc_cols
- 1) {
589 ch
= (char)get_char(vc
, (u_short
*) tmp_pos
, &temp
);
590 if ((ch
== SPACE
) || ch
== 0
591 || (IS_WDLM(buf
[cnt
- 1]) && (ch
> SPACE
)))
599 static void say_word(struct vc_data
*vc
)
601 u_long cnt
= get_word(vc
);
602 u_short saved_punc_mask
= spk_punc_mask
;
606 spk_punc_mask
= PUNC
;
608 spkup_write(buf
, cnt
);
609 spk_punc_mask
= saved_punc_mask
;
612 static void say_prev_word(struct vc_data
*vc
)
616 u_short edge_said
= 0, last_state
= 0, state
= 0;
622 announce_edge(vc
, edge_top
);
627 edge_said
= edge_quiet
;
632 edge_said
= edge_top
;
635 if (edge_said
!= edge_quiet
)
636 edge_said
= edge_left
;
640 spk_x
= vc
->vc_cols
- 1;
644 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
645 if (ch
== SPACE
|| ch
== 0)
647 else if (IS_WDLM(ch
))
651 if (state
< last_state
) {
658 if (spk_x
== 0 && edge_said
== edge_quiet
)
659 edge_said
= edge_left
;
660 if (edge_said
> 0 && edge_said
< edge_quiet
)
661 announce_edge(vc
, edge_said
);
665 static void say_next_word(struct vc_data
*vc
)
669 u_short edge_said
= 0, last_state
= 2, state
= 0;
672 if (spk_x
== vc
->vc_cols
- 1 && spk_y
== vc
->vc_rows
- 1) {
673 announce_edge(vc
, edge_bottom
);
677 ch
= (char)get_char(vc
, (u_short
*) spk_pos
, &temp
);
678 if (ch
== SPACE
|| ch
== 0)
680 else if (IS_WDLM(ch
))
684 if (state
> last_state
)
686 if (spk_x
>= vc
->vc_cols
- 1) {
687 if (spk_y
== vc
->vc_rows
- 1) {
688 edge_said
= edge_bottom
;
694 edge_said
= edge_right
;
701 announce_edge(vc
, edge_said
);
705 static void spell_word(struct vc_data
*vc
)
707 static char const *delay_str
[] = { "", ",", ".", ". .", ". . ." };
708 char *cp
= buf
, *str_cap
= spk_str_caps_stop
;
709 char *cp1
, *last_cap
= spk_str_caps_stop
;
714 while ((ch
= (u_char
) *cp
)) {
716 synth_printf(" %s ", delay_str
[spk_spell_delay
]);
717 if (IS_CHAR(ch
, B_CAP
)) {
718 str_cap
= spk_str_caps_start
;
719 if (*spk_str_caps_stop
)
721 else /* synth has no pitch */
722 last_cap
= spk_str_caps_stop
;
724 str_cap
= spk_str_caps_stop
;
725 if (str_cap
!= last_cap
) {
726 synth_printf("%s", str_cap
);
729 if (this_speakup_key
== SPELL_PHONETIC
730 && (isascii(ch
) && isalpha(ch
))) {
732 cp1
= phonetic
[--ch
];
734 cp1
= spk_characters
[ch
];
736 synth_printf("%s", spk_msg_get(MSG_CTRL
));
740 synth_printf("%s", cp1
);
743 if (str_cap
!= spk_str_caps_stop
)
744 synth_printf("%s", spk_str_caps_stop
);
747 static int get_line(struct vc_data
*vc
)
749 u_long tmp
= spk_pos
- (spk_x
* 2);
753 spk_old_attr
= spk_attr
;
754 spk_attr
= get_attributes(vc
, (u_short
*)spk_pos
);
755 for (i
= 0; i
< vc
->vc_cols
; i
++) {
756 buf
[i
] = (u_char
) get_char(vc
, (u_short
*) tmp
, &tmp2
);
759 for (--i
; i
>= 0; i
--)
765 static void say_line(struct vc_data
*vc
)
767 int i
= get_line(vc
);
769 u_short saved_punc_mask
= spk_punc_mask
;
772 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
776 if (this_speakup_key
== SAY_LINE_INDENT
) {
780 synth_printf("%d, ", (cp
- buf
) + 1);
782 spk_punc_mask
= spk_punc_masks
[spk_reading_punc
];
784 spk_punc_mask
= saved_punc_mask
;
787 static void say_prev_line(struct vc_data
*vc
)
791 announce_edge(vc
, edge_top
);
795 spk_pos
-= vc
->vc_size_row
;
799 static void say_next_line(struct vc_data
*vc
)
802 if (spk_y
== vc
->vc_rows
- 1) {
803 announce_edge(vc
, edge_bottom
);
807 spk_pos
+= vc
->vc_size_row
;
811 static int say_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
816 u_short saved_punc_mask
= spk_punc_mask
;
818 spk_old_attr
= spk_attr
;
819 spk_attr
= get_attributes(vc
, (u_short
*)from
);
821 buf
[i
++] = (char)get_char(vc
, (u_short
*) from
, &tmp
);
823 if (i
>= vc
->vc_size_row
)
826 for (--i
; i
>= 0; i
--)
834 spk_punc_mask
= spk_punc_info
[spk_reading_punc
].mask
;
837 spk_punc_mask
= saved_punc_mask
;
841 static void say_line_from_to(struct vc_data
*vc
, u_long from
, u_long to
,
844 u_long start
= vc
->vc_origin
+ (spk_y
* vc
->vc_size_row
);
845 u_long end
= start
+ (to
* 2);
848 if (say_from_to(vc
, start
, end
, read_punc
) <= 0)
849 if (cursor_track
!= read_all_mode
)
850 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
853 /* Sentence Reading Commands */
855 static int currsentence
;
856 static int numsentences
[2];
857 static char *sentbufend
[2];
858 static char *sentmarks
[2][10];
861 static char sentbuf
[2][256];
863 static int say_sentence_num(int num
, int prev
)
866 currsentence
= num
+ 1;
867 if (prev
&& --bn
== -1)
870 if (num
> numsentences
[bn
])
873 spkup_write(sentmarks
[bn
][num
], sentbufend
[bn
] - sentmarks
[bn
][num
]);
877 static int get_sentence_buf(struct vc_data
*vc
, int read_punc
)
887 start
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
);
888 end
= vc
->vc_origin
+ ((spk_y
) * vc
->vc_size_row
) + vc
->vc_cols
* 2;
890 numsentences
[bn
] = 0;
891 sentmarks
[bn
][0] = &sentbuf
[bn
][0];
893 spk_old_attr
= spk_attr
;
894 spk_attr
= get_attributes(vc
, (u_short
*)start
);
896 while (start
< end
) {
897 sentbuf
[bn
][i
] = (char)get_char(vc
, (u_short
*) start
, &tmp
);
899 if (sentbuf
[bn
][i
] == SPACE
&& sentbuf
[bn
][i
- 1] == '.'
900 && numsentences
[bn
] < 9) {
901 /* Sentence Marker */
903 sentmarks
[bn
][numsentences
[bn
]] =
909 if (i
>= vc
->vc_size_row
)
913 for (--i
; i
>= 0; i
--)
914 if (sentbuf
[bn
][i
] != SPACE
)
920 sentbuf
[bn
][++i
] = SPACE
;
921 sentbuf
[bn
][++i
] = '\0';
923 sentbufend
[bn
] = &sentbuf
[bn
][i
];
924 return numsentences
[bn
];
927 static void say_screen_from_to(struct vc_data
*vc
, u_long from
, u_long to
)
929 u_long start
= vc
->vc_origin
, end
;
932 start
+= from
* vc
->vc_size_row
;
933 if (to
> vc
->vc_rows
)
935 end
= vc
->vc_origin
+ (to
* vc
->vc_size_row
);
936 for (from
= start
; from
< end
; from
= to
) {
937 to
= from
+ vc
->vc_size_row
;
938 say_from_to(vc
, from
, to
, 1);
942 static void say_screen(struct vc_data
*vc
)
944 say_screen_from_to(vc
, 0, vc
->vc_rows
);
947 static void speakup_win_say(struct vc_data
*vc
)
949 u_long start
, end
, from
, to
;
952 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
955 start
= vc
->vc_origin
+ (win_top
* vc
->vc_size_row
);
956 end
= vc
->vc_origin
+ (win_bottom
* vc
->vc_size_row
);
957 while (start
<= end
) {
958 from
= start
+ (win_left
* 2);
959 to
= start
+ (win_right
* 2);
960 say_from_to(vc
, from
, to
, 1);
961 start
+= vc
->vc_size_row
;
965 static void top_edge(struct vc_data
*vc
)
968 spk_pos
= vc
->vc_origin
+ 2 * spk_x
;
973 static void bottom_edge(struct vc_data
*vc
)
976 spk_pos
+= (vc
->vc_rows
- spk_y
- 1) * vc
->vc_size_row
;
977 spk_y
= vc
->vc_rows
- 1;
981 static void left_edge(struct vc_data
*vc
)
984 spk_pos
-= spk_x
* 2;
989 static void right_edge(struct vc_data
*vc
)
992 spk_pos
+= (vc
->vc_cols
- spk_x
- 1) * 2;
993 spk_x
= vc
->vc_cols
- 1;
997 static void say_first_char(struct vc_data
*vc
)
999 int i
, len
= get_line(vc
);
1004 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1007 for (i
= 0; i
< len
; i
++)
1008 if (buf
[i
] != SPACE
)
1011 spk_pos
-= (spk_x
- i
) * 2;
1013 synth_printf("%d, ", ++i
);
1017 static void say_last_char(struct vc_data
*vc
)
1019 int len
= get_line(vc
);
1024 synth_printf("%s\n", spk_msg_get(MSG_BLANK
));
1028 spk_pos
-= (spk_x
- len
) * 2;
1030 synth_printf("%d, ", ++len
);
1034 static void say_position(struct vc_data
*vc
)
1036 synth_printf(spk_msg_get(MSG_POS_INFO
), spk_y
+ 1, spk_x
+ 1,
1041 /* Added by brianb */
1042 static void say_char_num(struct vc_data
*vc
)
1045 u_short ch
= get_char(vc
, (u_short
*) spk_pos
, &tmp
);
1048 synth_printf(spk_msg_get(MSG_CHAR_INFO
), ch
, ch
);
1051 /* these are stub functions to keep keyboard.c happy. */
1053 static void say_from_top(struct vc_data
*vc
)
1055 say_screen_from_to(vc
, 0, spk_y
);
1058 static void say_to_bottom(struct vc_data
*vc
)
1060 say_screen_from_to(vc
, spk_y
, vc
->vc_rows
);
1063 static void say_from_left(struct vc_data
*vc
)
1065 say_line_from_to(vc
, 0, spk_x
, 1);
1068 static void say_to_right(struct vc_data
*vc
)
1070 say_line_from_to(vc
, spk_x
, vc
->vc_cols
, 1);
1073 /* end of stub functions. */
1075 static void spkup_write(const char *in_buf
, int count
)
1077 static int rep_count
;
1078 static u_char ch
= '\0', old_ch
= '\0';
1079 static u_short char_type
, last_type
;
1080 int in_count
= count
;
1084 if (cursor_track
== read_all_mode
) {
1085 /* Insert Sentence Index */
1086 if ((in_buf
== sentmarks
[bn
][currsentence
]) &&
1087 (currsentence
<= numsentences
[bn
]))
1088 synth_insert_next_index(currsentence
++);
1090 ch
= (u_char
) *in_buf
++;
1091 char_type
= spk_chartab
[ch
];
1092 if (ch
== old_ch
&& !(char_type
& B_NUM
)) {
1093 if (++rep_count
> 2)
1096 if ((last_type
& CH_RPT
) && rep_count
> 2) {
1098 synth_printf(spk_msg_get(MSG_REPEAT_DESC
),
1104 if (ch
== spk_lastkey
) {
1106 if (spk_key_echo
== 1 && ch
>= MINECHOCHAR
)
1108 } else if (char_type
& B_ALPHA
) {
1109 if ((synth_flags
& SF_DEC
) && (last_type
& PUNC
))
1110 synth_buffer_add(SPACE
);
1111 synth_printf("%c", ch
);
1112 } else if (char_type
& B_NUM
) {
1114 synth_printf("%c", ch
);
1115 } else if (char_type
& spk_punc_mask
) {
1117 char_type
&= ~PUNC
; /* for dec nospell processing */
1118 } else if (char_type
& SYNTH_OK
) {
1119 /* these are usually puncts like . and , which synth
1120 * needs for expression.
1121 * suppress multiple to get rid of long pauses and
1122 * clear repeat count
1124 * repeats on you don't get nothing repeated count
1127 synth_printf("%c", ch
);
1131 /* send space and record position, if next is num overwrite space */
1133 synth_buffer_add(SPACE
);
1138 last_type
= char_type
;
1141 if (in_count
> 2 && rep_count
> 2) {
1142 if (last_type
& CH_RPT
) {
1144 synth_printf(spk_msg_get(MSG_REPEAT_DESC2
),
1152 static const int NUM_CTL_LABELS
= (MSG_CTL_END
- MSG_CTL_START
+ 1);
1154 static void read_all_doc(struct vc_data
*vc
);
1155 static void cursor_done(u_long data
);
1156 static DEFINE_TIMER(cursor_timer
, cursor_done
, 0, 0);
1158 static void do_handle_shift(struct vc_data
*vc
, u_char value
, char up_flag
)
1160 unsigned long flags
;
1162 if (synth
== NULL
|| up_flag
|| spk_killed
)
1164 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1165 if (cursor_track
== read_all_mode
) {
1168 del_timer(&cursor_timer
);
1169 spk_shut_up
&= 0xfe;
1174 del_timer(&cursor_timer
);
1175 cursor_track
= prev_cursor_track
;
1176 spk_shut_up
&= 0xfe;
1181 spk_shut_up
&= 0xfe;
1184 if (spk_say_ctrl
&& value
< NUM_CTL_LABELS
)
1185 synth_printf("%s", spk_msg_get(MSG_CTL_START
+ value
));
1186 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1189 static void do_handle_latin(struct vc_data
*vc
, u_char value
, char up_flag
)
1191 unsigned long flags
;
1193 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1195 spk_lastkey
= spk_keydown
= 0;
1196 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1199 if (synth
== NULL
|| spk_killed
) {
1200 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1203 spk_shut_up
&= 0xfe;
1204 spk_lastkey
= value
;
1207 if (spk_key_echo
== 2 && value
>= MINECHOCHAR
)
1209 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1212 int spk_set_key_info(const u_char
*key_info
, u_char
*k_buffer
)
1214 int i
= 0, states
, key_data_len
;
1215 const u_char
*cp
= key_info
;
1216 u_char
*cp1
= k_buffer
;
1217 u_char ch
, version
, num_keys
;
1220 if (version
!= KEY_MAP_VER
)
1223 states
= (int)cp
[1];
1224 key_data_len
= (states
+ 1) * (num_keys
+ 1);
1225 if (key_data_len
+ SHIFT_TBL_SIZE
+ 4 >= sizeof(spk_key_buf
))
1227 memset(k_buffer
, 0, SHIFT_TBL_SIZE
);
1228 memset(spk_our_keys
, 0, sizeof(spk_our_keys
));
1229 spk_shift_table
= k_buffer
;
1230 spk_our_keys
[0] = spk_shift_table
;
1231 cp1
+= SHIFT_TBL_SIZE
;
1232 memcpy(cp1
, cp
, key_data_len
+ 3);
1233 /* get num_keys, states and data */
1234 cp1
+= 2; /* now pointing at shift states */
1235 for (i
= 1; i
<= states
; i
++) {
1237 if (ch
>= SHIFT_TBL_SIZE
)
1239 spk_shift_table
[ch
] = i
;
1241 keymap_flags
= *cp1
++;
1242 while ((ch
= *cp1
)) {
1245 spk_our_keys
[ch
] = cp1
;
1251 static struct var_t spk_vars
[] = {
1252 /* bell must be first to set high limit */
1253 {BELL_POS
, .u
.n
= {NULL
, 0, 0, 0, 0, 0, NULL
} },
1254 {SPELL_DELAY
, .u
.n
= {NULL
, 0, 0, 4, 0, 0, NULL
} },
1255 {ATTRIB_BLEEP
, .u
.n
= {NULL
, 1, 0, 3, 0, 0, NULL
} },
1256 {BLEEPS
, .u
.n
= {NULL
, 3, 0, 3, 0, 0, NULL
} },
1257 {BLEEP_TIME
, .u
.n
= {NULL
, 30, 1, 200, 0, 0, NULL
} },
1258 {PUNC_LEVEL
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1259 {READING_PUNC
, .u
.n
= {NULL
, 1, 0, 4, 0, 0, NULL
} },
1260 {CURSOR_TIME
, .u
.n
= {NULL
, 120, 50, 600, 0, 0, NULL
} },
1261 {SAY_CONTROL
, TOGGLE_0
},
1262 {SAY_WORD_CTL
, TOGGLE_0
},
1263 {NO_INTERRUPT
, TOGGLE_0
},
1264 {KEY_ECHO
, .u
.n
= {NULL
, 1, 0, 2, 0, 0, NULL
} },
1268 static void toggle_cursoring(struct vc_data
*vc
)
1270 if (cursor_track
== read_all_mode
)
1271 cursor_track
= prev_cursor_track
;
1272 if (++cursor_track
>= CT_Max
)
1274 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START
+ cursor_track
));
1277 void spk_reset_default_chars(void)
1281 /* First, free any non-default */
1282 for (i
= 0; i
< 256; i
++) {
1283 if ((spk_characters
[i
] != NULL
)
1284 && (spk_characters
[i
] != spk_default_chars
[i
]))
1285 kfree(spk_characters
[i
]);
1288 memcpy(spk_characters
, spk_default_chars
, sizeof(spk_default_chars
));
1291 void spk_reset_default_chartab(void)
1293 memcpy(spk_chartab
, default_chartab
, sizeof(default_chartab
));
1296 static const struct st_bits_data
*pb_edit
;
1298 static int edit_bits(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1300 short mask
= pb_edit
->mask
, ch_type
= spk_chartab
[ch
];
1302 if (type
!= KT_LATIN
|| (ch_type
& B_NUM
) || ch
< SPACE
)
1305 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE
));
1306 spk_special_handler
= NULL
;
1309 if (mask
< PUNC
&& !(ch_type
& PUNC
))
1311 spk_chartab
[ch
] ^= mask
;
1313 synth_printf(" %s\n",
1314 (spk_chartab
[ch
] & mask
) ? spk_msg_get(MSG_ON
) :
1315 spk_msg_get(MSG_OFF
));
1319 /* Allocation concurrency is protected by the console semaphore */
1320 static int speakup_allocate(struct vc_data
*vc
)
1324 vc_num
= vc
->vc_num
;
1325 if (speakup_console
[vc_num
] == NULL
) {
1326 speakup_console
[vc_num
] = kzalloc(sizeof(*speakup_console
[0]),
1328 if (speakup_console
[vc_num
] == NULL
)
1331 } else if (!spk_parked
)
1337 static void speakup_deallocate(struct vc_data
*vc
)
1341 vc_num
= vc
->vc_num
;
1342 kfree(speakup_console
[vc_num
]);
1343 speakup_console
[vc_num
] = NULL
;
1346 static u_char is_cursor
;
1347 static u_long old_cursor_pos
, old_cursor_x
, old_cursor_y
;
1348 static int cursor_con
;
1350 static void reset_highlight_buffers(struct vc_data
*);
1352 static int read_all_key
;
1354 static void start_read_all_timer(struct vc_data
*vc
, int command
);
1368 static void kbd_fakekey2(struct vc_data
*vc
, int command
)
1370 del_timer(&cursor_timer
);
1371 speakup_fake_down_arrow();
1372 start_read_all_timer(vc
, command
);
1375 static void read_all_doc(struct vc_data
*vc
)
1377 if ((vc
->vc_num
!= fg_console
) || synth
== NULL
|| spk_shut_up
)
1379 if (!synth_supports_indexing())
1381 if (cursor_track
!= read_all_mode
)
1382 prev_cursor_track
= cursor_track
;
1383 cursor_track
= read_all_mode
;
1384 spk_reset_index_count(0);
1385 if (get_sentence_buf(vc
, 0) == -1)
1386 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1388 say_sentence_num(0, 0);
1389 synth_insert_next_index(0);
1390 start_read_all_timer(vc
, RA_TIMER
);
1394 static void stop_read_all(struct vc_data
*vc
)
1396 del_timer(&cursor_timer
);
1397 cursor_track
= prev_cursor_track
;
1398 spk_shut_up
&= 0xfe;
1402 static void start_read_all_timer(struct vc_data
*vc
, int command
)
1404 struct var_t
*cursor_timeout
;
1406 cursor_con
= vc
->vc_num
;
1407 read_all_key
= command
;
1408 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1409 mod_timer(&cursor_timer
,
1410 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1413 static void handle_cursor_read_all(struct vc_data
*vc
, int command
)
1415 int indcount
, sentcount
, rv
, sn
;
1419 /* Get Current Sentence */
1420 spk_get_index_count(&indcount
, &sentcount
);
1421 /*printk("%d %d ", indcount, sentcount); */
1422 spk_reset_index_count(sentcount
+ 1);
1423 if (indcount
== 1) {
1424 if (!say_sentence_num(sentcount
+ 1, 0)) {
1425 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1428 synth_insert_next_index(0);
1431 if (!say_sentence_num(sentcount
+ 1, 1)) {
1433 spk_reset_index_count(sn
);
1435 synth_insert_next_index(0);
1436 if (!say_sentence_num(sn
, 0)) {
1437 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1440 synth_insert_next_index(0);
1442 start_read_all_timer(vc
, RA_TIMER
);
1452 if (get_sentence_buf(vc
, 0) == -1) {
1453 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1455 say_sentence_num(0, 0);
1456 synth_insert_next_index(0);
1457 start_read_all_timer(vc
, RA_TIMER
);
1460 case RA_FIND_NEXT_SENT
:
1461 rv
= get_sentence_buf(vc
, 0);
1465 kbd_fakekey2(vc
, RA_FIND_NEXT_SENT
);
1467 say_sentence_num(1, 0);
1468 synth_insert_next_index(0);
1469 start_read_all_timer(vc
, RA_TIMER
);
1472 case RA_FIND_PREV_SENT
:
1475 spk_get_index_count(&indcount
, &sentcount
);
1477 kbd_fakekey2(vc
, RA_DOWN_ARROW
);
1479 start_read_all_timer(vc
, RA_TIMER
);
1484 static int pre_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1486 unsigned long flags
;
1488 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1489 if (cursor_track
== read_all_mode
) {
1491 if (synth
== NULL
|| up_flag
|| spk_shut_up
) {
1492 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1495 del_timer(&cursor_timer
);
1496 spk_shut_up
&= 0xfe;
1498 start_read_all_timer(vc
, value
+ 1);
1499 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1502 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1506 static void do_handle_cursor(struct vc_data
*vc
, u_char value
, char up_flag
)
1508 unsigned long flags
;
1509 struct var_t
*cursor_timeout
;
1511 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1513 if (synth
== NULL
|| up_flag
|| spk_shut_up
|| cursor_track
== CT_Off
) {
1514 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1517 spk_shut_up
&= 0xfe;
1520 /* the key press flushes if !no_inter but we want to flush on cursor
1521 * moves regardless of no_inter state
1523 is_cursor
= value
+ 1;
1524 old_cursor_pos
= vc
->vc_pos
;
1525 old_cursor_x
= vc
->vc_x
;
1526 old_cursor_y
= vc
->vc_y
;
1527 speakup_console
[vc
->vc_num
]->ht
.cy
= vc
->vc_y
;
1528 cursor_con
= vc
->vc_num
;
1529 if (cursor_track
== CT_Highlight
)
1530 reset_highlight_buffers(vc
);
1531 cursor_timeout
= spk_get_var(CURSOR_TIME
);
1532 mod_timer(&cursor_timer
,
1533 jiffies
+ msecs_to_jiffies(cursor_timeout
->u
.n
.value
));
1534 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1537 static void update_color_buffer(struct vc_data
*vc
, const char *ic
, int len
)
1540 int vc_num
= vc
->vc_num
;
1542 bi
= (vc
->vc_attr
& 0x70) >> 4;
1543 hi
= speakup_console
[vc_num
]->ht
.highsize
[bi
];
1546 if (speakup_console
[vc_num
]->ht
.highsize
[bi
] == 0) {
1547 speakup_console
[vc_num
]->ht
.rpos
[bi
] = vc
->vc_pos
;
1548 speakup_console
[vc_num
]->ht
.rx
[bi
] = vc
->vc_x
;
1549 speakup_console
[vc_num
]->ht
.ry
[bi
] = vc
->vc_y
;
1551 while ((hi
< COLOR_BUFFER_SIZE
) && (i
< len
)) {
1552 if ((ic
[i
] > 32) && (ic
[i
] < 127)) {
1553 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] = ic
[i
];
1555 } else if ((ic
[i
] == 32) && (hi
!= 0)) {
1556 if (speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
- 1] !=
1558 speakup_console
[vc_num
]->ht
.highbuf
[bi
][hi
] =
1565 speakup_console
[vc_num
]->ht
.highsize
[bi
] = hi
;
1568 static void reset_highlight_buffers(struct vc_data
*vc
)
1571 int vc_num
= vc
->vc_num
;
1573 for (i
= 0; i
< 8; i
++)
1574 speakup_console
[vc_num
]->ht
.highsize
[i
] = 0;
1577 static int count_highlight_color(struct vc_data
*vc
)
1581 int vc_num
= vc
->vc_num
;
1583 u16
*start
= (u16
*) vc
->vc_origin
;
1585 for (i
= 0; i
< 8; i
++)
1586 speakup_console
[vc_num
]->ht
.bgcount
[i
] = 0;
1588 for (i
= 0; i
< vc
->vc_rows
; i
++) {
1589 u16
*end
= start
+ vc
->vc_cols
* 2;
1592 for (ptr
= start
; ptr
< end
; ptr
++) {
1593 ch
= get_attributes(vc
, ptr
);
1594 bg
= (ch
& 0x70) >> 4;
1595 speakup_console
[vc_num
]->ht
.bgcount
[bg
]++;
1597 start
+= vc
->vc_size_row
;
1601 for (i
= 0; i
< 8; i
++)
1602 if (speakup_console
[vc_num
]->ht
.bgcount
[i
] > 0)
1607 static int get_highlight_color(struct vc_data
*vc
)
1610 unsigned int cptr
[8];
1611 int vc_num
= vc
->vc_num
;
1613 for (i
= 0; i
< 8; i
++)
1616 for (i
= 0; i
< 7; i
++)
1617 for (j
= i
+ 1; j
< 8; j
++)
1618 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] >
1619 speakup_console
[vc_num
]->ht
.bgcount
[cptr
[j
]])
1620 swap(cptr
[i
], cptr
[j
]);
1622 for (i
= 0; i
< 8; i
++)
1623 if (speakup_console
[vc_num
]->ht
.bgcount
[cptr
[i
]] != 0)
1624 if (speakup_console
[vc_num
]->ht
.highsize
[cptr
[i
]] > 0)
1629 static int speak_highlight(struct vc_data
*vc
)
1632 int vc_num
= vc
->vc_num
;
1634 if (count_highlight_color(vc
) == 1)
1636 hc
= get_highlight_color(vc
);
1638 d
= vc
->vc_y
- speakup_console
[vc_num
]->ht
.cy
;
1639 if ((d
== 1) || (d
== -1))
1640 if (speakup_console
[vc_num
]->ht
.ry
[hc
] != vc
->vc_y
)
1644 spkup_write(speakup_console
[vc_num
]->ht
.highbuf
[hc
],
1645 speakup_console
[vc_num
]->ht
.highsize
[hc
]);
1646 spk_pos
= spk_cp
= speakup_console
[vc_num
]->ht
.rpos
[hc
];
1647 spk_x
= spk_cx
= speakup_console
[vc_num
]->ht
.rx
[hc
];
1648 spk_y
= spk_cy
= speakup_console
[vc_num
]->ht
.ry
[hc
];
1654 static void cursor_done(u_long data
)
1656 struct vc_data
*vc
= vc_cons
[cursor_con
].d
;
1657 unsigned long flags
;
1659 del_timer(&cursor_timer
);
1660 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1661 if (cursor_con
!= fg_console
) {
1667 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1668 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1669 spk_keydown
= is_cursor
= 0;
1673 if (cursor_track
== read_all_mode
) {
1674 handle_cursor_read_all(vc
, read_all_key
);
1677 if (cursor_track
== CT_Highlight
) {
1678 if (speak_highlight(vc
)) {
1679 spk_keydown
= is_cursor
= 0;
1683 if (cursor_track
== CT_Window
)
1684 speakup_win_say(vc
);
1685 else if (is_cursor
== 1 || is_cursor
== 4)
1686 say_line_from_to(vc
, 0, vc
->vc_cols
, 0);
1689 spk_keydown
= is_cursor
= 0;
1691 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1694 /* called by: vt_notifier_call() */
1695 static void speakup_bs(struct vc_data
*vc
)
1697 unsigned long flags
;
1699 if (!speakup_console
[vc
->vc_num
])
1701 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1702 /* Speakup output, discard */
1706 if (spk_shut_up
|| synth
== NULL
) {
1707 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1710 if (vc
->vc_num
== fg_console
&& spk_keydown
) {
1715 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1718 /* called by: vt_notifier_call() */
1719 static void speakup_con_write(struct vc_data
*vc
, const char *str
, int len
)
1721 unsigned long flags
;
1723 if ((vc
->vc_num
!= fg_console
) || spk_shut_up
|| synth
== NULL
)
1725 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1726 /* Speakup output, discard */
1728 if (spk_bell_pos
&& spk_keydown
&& (vc
->vc_x
== spk_bell_pos
- 1))
1730 if ((is_cursor
) || (cursor_track
== read_all_mode
)) {
1731 if (cursor_track
== CT_Highlight
)
1732 update_color_buffer(vc
, str
, len
);
1733 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1737 if (vc
->vc_x
>= win_left
&& vc
->vc_x
<= win_right
&&
1738 vc
->vc_y
>= win_top
&& vc
->vc_y
<= win_bottom
) {
1739 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1744 spkup_write(str
, len
);
1745 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1748 static void speakup_con_update(struct vc_data
*vc
)
1750 unsigned long flags
;
1752 if (speakup_console
[vc
->vc_num
] == NULL
|| spk_parked
)
1754 if (!spin_trylock_irqsave(&speakup_info
.spinlock
, flags
))
1755 /* Speakup output, discard */
1758 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1761 static void do_handle_spec(struct vc_data
*vc
, u_char value
, char up_flag
)
1763 unsigned long flags
;
1767 if (synth
== NULL
|| up_flag
|| spk_killed
)
1769 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
1770 spk_shut_up
&= 0xfe;
1775 label
= spk_msg_get(MSG_KEYNAME_CAPSLOCK
);
1776 on_off
= vt_get_leds(fg_console
, VC_CAPSLOCK
);
1779 label
= spk_msg_get(MSG_KEYNAME_NUMLOCK
);
1780 on_off
= vt_get_leds(fg_console
, VC_NUMLOCK
);
1783 label
= spk_msg_get(MSG_KEYNAME_SCROLLLOCK
);
1784 on_off
= vt_get_leds(fg_console
, VC_SCROLLOCK
);
1785 if (speakup_console
[vc
->vc_num
])
1786 speakup_console
[vc
->vc_num
]->tty_stopped
= on_off
;
1790 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1794 synth_printf("%s %s\n",
1795 label
, spk_msg_get(MSG_STATUS_START
+ on_off
));
1796 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
1799 static int inc_dec_var(u_char value
)
1801 struct st_var_header
*p_header
;
1802 struct var_t
*var_data
;
1806 int var_id
= (int)value
- VAR_START
;
1807 int how
= (var_id
& 1) ? E_INC
: E_DEC
;
1809 var_id
= var_id
/ 2 + FIRST_SET_VAR
;
1810 p_header
= spk_get_var_header(var_id
);
1811 if (p_header
== NULL
)
1813 if (p_header
->var_type
!= VAR_NUM
)
1815 var_data
= p_header
->data
;
1816 if (spk_set_num_var(1, p_header
, how
) != 0)
1818 if (!spk_close_press
) {
1819 for (pn
= p_header
->name
; *pn
; pn
++) {
1826 snprintf(cp
, sizeof(num_buf
) - (cp
- num_buf
), " %d ",
1827 var_data
->u
.n
.value
);
1828 synth_printf("%s", num_buf
);
1832 static void speakup_win_set(struct vc_data
*vc
)
1836 if (win_start
> 1) {
1837 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET
));
1840 if (spk_x
< win_left
|| spk_y
< win_top
) {
1841 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START
));
1844 if (win_start
&& spk_x
== win_left
&& spk_y
== win_top
) {
1846 win_right
= vc
->vc_cols
- 1;
1848 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_LINE
),
1858 snprintf(info
, sizeof(info
), spk_msg_get(MSG_WINDOW_BOUNDARY
),
1860 spk_msg_get(MSG_END
) : spk_msg_get(MSG_START
),
1861 (int)spk_y
+ 1, (int)spk_x
+ 1);
1863 synth_printf("%s\n", info
);
1867 static void speakup_win_clear(struct vc_data
*vc
)
1869 win_top
= win_bottom
= 0;
1870 win_left
= win_right
= 0;
1872 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED
));
1875 static void speakup_win_enable(struct vc_data
*vc
)
1877 if (win_start
< 2) {
1878 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW
));
1883 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED
));
1885 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED
));
1888 static void speakup_bits(struct vc_data
*vc
)
1890 int val
= this_speakup_key
- (FIRST_EDIT_BITS
- 1);
1892 if (spk_special_handler
!= NULL
|| val
< 1 || val
> 6) {
1893 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1896 pb_edit
= &spk_punc_info
[val
];
1897 synth_printf(spk_msg_get(MSG_EDIT_PROMPT
), pb_edit
->name
);
1898 spk_special_handler
= edit_bits
;
1901 static int handle_goto(struct vc_data
*vc
, u_char type
, u_char ch
, u_short key
)
1903 static u_char goto_buf
[8];
1908 if (type
== KT_SPKUP
&& ch
== SPEAKUP_GOTO
)
1910 if (type
== KT_LATIN
&& ch
== '\n')
1917 ch
= goto_buf
[--num
];
1918 goto_buf
[num
] = '\0';
1919 spkup_write(&ch
, 1);
1922 if (ch
< '+' || ch
> 'y')
1924 goto_buf
[num
++] = ch
;
1925 goto_buf
[num
] = '\0';
1926 spkup_write(&ch
, 1);
1927 maxlen
= (*goto_buf
>= '0') ? 3 : 4;
1928 if ((ch
== '+' || ch
== '-') && num
== 1)
1930 if (ch
>= '0' && ch
<= '9' && num
< maxlen
)
1932 if (num
< maxlen
- 1 || num
> maxlen
)
1934 if (ch
< 'x' || ch
> 'y') {
1937 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED
));
1938 goto_buf
[num
= 0] = '\0';
1939 spk_special_handler
= NULL
;
1943 goto_pos
= simple_strtoul(goto_buf
, &cp
, 10);
1946 if (*goto_buf
< '0')
1948 else if (goto_pos
> 0)
1951 if (goto_pos
>= vc
->vc_cols
)
1952 goto_pos
= vc
->vc_cols
- 1;
1955 if (*goto_buf
< '0')
1957 else if (goto_pos
> 0)
1960 if (goto_pos
>= vc
->vc_rows
)
1961 goto_pos
= vc
->vc_rows
- 1;
1964 goto_buf
[num
= 0] = '\0';
1966 spk_special_handler
= NULL
;
1969 spk_pos
-= spk_x
* 2;
1971 spk_pos
+= goto_pos
* 2;
1975 spk_pos
= vc
->vc_origin
+ (goto_pos
* vc
->vc_size_row
);
1981 static void speakup_goto(struct vc_data
*vc
)
1983 if (spk_special_handler
!= NULL
) {
1984 synth_printf("%s\n", spk_msg_get(MSG_ERROR
));
1987 synth_printf("%s\n", spk_msg_get(MSG_GOTO
));
1988 spk_special_handler
= handle_goto
;
1991 static void speakup_help(struct vc_data
*vc
)
1993 spk_handle_help(vc
, KT_SPKUP
, SPEAKUP_HELP
, 0);
1996 static void do_nothing(struct vc_data
*vc
)
1998 return; /* flush done in do_spkup */
2001 static u_char key_speakup
, spk_key_locked
;
2003 static void speakup_lock(struct vc_data
*vc
)
2005 if (!spk_key_locked
)
2006 spk_key_locked
= key_speakup
= 16;
2008 spk_key_locked
= key_speakup
= 0;
2011 typedef void (*spkup_hand
) (struct vc_data
*);
2012 static spkup_hand spkup_handler
[] = {
2013 /* must be ordered same as defines in speakup.h */
2014 do_nothing
, speakup_goto
, speech_kill
, speakup_shut_up
,
2015 speakup_cut
, speakup_paste
, say_first_char
, say_last_char
,
2016 say_char
, say_prev_char
, say_next_char
,
2017 say_word
, say_prev_word
, say_next_word
,
2018 say_line
, say_prev_line
, say_next_line
,
2019 top_edge
, bottom_edge
, left_edge
, right_edge
,
2020 spell_word
, spell_word
, say_screen
,
2021 say_position
, say_attributes
,
2022 speakup_off
, speakup_parked
, say_line
, /* this is for indent */
2023 say_from_top
, say_to_bottom
,
2024 say_from_left
, say_to_right
,
2025 say_char_num
, speakup_bits
, speakup_bits
, say_phonetic_char
,
2026 speakup_bits
, speakup_bits
, speakup_bits
,
2027 speakup_win_set
, speakup_win_clear
, speakup_win_enable
, speakup_win_say
,
2028 speakup_lock
, speakup_help
, toggle_cursoring
, read_all_doc
, NULL
2031 static void do_spkup(struct vc_data
*vc
, u_char value
)
2033 if (spk_killed
&& value
!= SPEECH_KILL
)
2037 spk_shut_up
&= 0xfe;
2038 this_speakup_key
= value
;
2039 if (value
< SPKUP_MAX_FUNC
&& spkup_handler
[value
]) {
2041 (*spkup_handler
[value
]) (vc
);
2043 if (inc_dec_var(value
) < 0)
2048 static const char *pad_chars
= "0123456789+-*/\015,.?()";
2051 speakup_key(struct vc_data
*vc
, int shift_state
, int keycode
, u_short keysym
,
2054 unsigned long flags
;
2057 u_char type
= KTYP(keysym
), value
= KVAL(keysym
), new_key
= 0;
2058 u_char shift_info
, offset
;
2064 spin_lock_irqsave(&speakup_info
.spinlock
, flags
);
2069 && (vt_get_leds(fg_console
, VC_NUMLOCK
))) {
2074 value
= spk_lastkey
= pad_chars
[value
];
2079 if (keycode
>= MAX_KEY
)
2081 key_info
= spk_our_keys
[keycode
];
2084 /* Check valid read all mode keys */
2085 if ((cursor_track
== read_all_mode
) && (!up_flag
)) {
2099 shift_info
= (shift_state
& 0x0f) + key_speakup
;
2100 offset
= spk_shift_table
[shift_info
];
2102 new_key
= key_info
[offset
];
2105 if (new_key
== SPK_KEY
) {
2106 if (!spk_key_locked
)
2107 key_speakup
= (up_flag
) ? 0 : 16;
2108 if (up_flag
|| spk_killed
)
2110 spk_shut_up
&= 0xfe;
2116 if (last_keycode
== keycode
&&
2117 time_after(last_spk_jiffy
+ MAX_DELAY
, jiffies
)) {
2118 spk_close_press
= 1;
2119 offset
= spk_shift_table
[shift_info
+ 32];
2121 if (offset
&& key_info
[offset
])
2122 new_key
= key_info
[offset
];
2124 last_keycode
= keycode
;
2125 last_spk_jiffy
= jiffies
;
2131 if (type
== KT_SPKUP
&& spk_special_handler
== NULL
) {
2132 do_spkup(vc
, new_key
);
2133 spk_close_press
= 0;
2137 if (up_flag
|| spk_killed
|| type
== KT_SHIFT
)
2139 spk_shut_up
&= 0xfe;
2140 kh
= (value
== KVAL(K_DOWN
))
2141 || (value
== KVAL(K_UP
))
2142 || (value
== KVAL(K_LEFT
))
2143 || (value
== KVAL(K_RIGHT
));
2144 if ((cursor_track
!= read_all_mode
) || !kh
)
2147 if (spk_special_handler
) {
2148 if (type
== KT_SPEC
&& value
== 1) {
2151 } else if (type
== KT_LETTER
)
2153 else if (value
== 0x7f)
2154 value
= 8; /* make del = backspace */
2155 ret
= (*spk_special_handler
) (vc
, type
, value
, keycode
);
2156 spk_close_press
= 0;
2163 spin_unlock_irqrestore(&speakup_info
.spinlock
, flags
);
2167 static int keyboard_notifier_call(struct notifier_block
*nb
,
2168 unsigned long code
, void *_param
)
2170 struct keyboard_notifier_param
*param
= _param
;
2171 struct vc_data
*vc
= param
->vc
;
2172 int up
= !param
->down
;
2173 int ret
= NOTIFY_OK
;
2174 static int keycode
; /* to hold the current keycode */
2176 if (vc
->vc_mode
== KD_GRAPHICS
)
2180 * First, determine whether we are handling a fake keypress on
2181 * the current processor. If we are, then return NOTIFY_OK,
2182 * to pass the keystroke up the chain. This prevents us from
2183 * trying to take the Speakup lock while it is held by the
2184 * processor on which the simulated keystroke was generated.
2185 * Also, the simulated keystrokes should be ignored by Speakup.
2188 if (speakup_fake_key_pressed())
2193 /* speakup requires keycode and keysym currently */
2194 keycode
= param
->value
;
2196 case KBD_UNBOUND_KEYCODE
:
2203 if (speakup_key(vc
, param
->shift
, keycode
, param
->value
, up
))
2205 else if (KTYP(param
->value
) == KT_CUR
)
2206 ret
= pre_handle_cursor(vc
, KVAL(param
->value
), up
);
2208 case KBD_POST_KEYSYM
:{
2209 unsigned char type
= KTYP(param
->value
) - 0xf0;
2210 unsigned char val
= KVAL(param
->value
);
2214 do_handle_shift(vc
, val
, up
);
2218 do_handle_latin(vc
, val
, up
);
2221 do_handle_cursor(vc
, val
, up
);
2224 do_handle_spec(vc
, val
, up
);
2233 static int vt_notifier_call(struct notifier_block
*nb
,
2234 unsigned long code
, void *_param
)
2236 struct vt_notifier_param
*param
= _param
;
2237 struct vc_data
*vc
= param
->vc
;
2241 if (vc
->vc_mode
== KD_TEXT
)
2242 speakup_allocate(vc
);
2245 speakup_deallocate(vc
);
2248 if (param
->c
== '\b')
2250 else if (param
->c
< 0x100) {
2253 speakup_con_write(vc
, &d
, 1);
2257 speakup_con_update(vc
);
2263 /* called by: module_exit() */
2264 static void __exit
speakup_exit(void)
2268 unregister_keyboard_notifier(&keyboard_notifier_block
);
2269 unregister_vt_notifier(&vt_notifier_block
);
2270 speakup_unregister_devsynth();
2271 speakup_cancel_paste();
2272 del_timer(&cursor_timer
);
2273 kthread_stop(speakup_task
);
2274 speakup_task
= NULL
;
2275 mutex_lock(&spk_mutex
);
2277 mutex_unlock(&spk_mutex
);
2279 speakup_kobj_exit();
2281 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2282 kfree(speakup_console
[i
]);
2284 speakup_remove_virtual_keyboard();
2286 for (i
= 0; i
< MAXVARS
; i
++)
2287 speakup_unregister_var(i
);
2289 for (i
= 0; i
< 256; i
++) {
2290 if (spk_characters
[i
] != spk_default_chars
[i
])
2291 kfree(spk_characters
[i
]);
2294 spk_free_user_msgs();
2297 /* call by: module_init() */
2298 static int __init
speakup_init(void)
2302 struct st_spk_t
*first_console
;
2303 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
2306 /* These first few initializations cannot fail. */
2307 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2308 spk_reset_default_chars();
2309 spk_reset_default_chartab();
2310 spk_strlwr(synth_name
);
2311 spk_vars
[0].u
.n
.high
= vc
->vc_cols
;
2312 for (var
= spk_vars
; var
->var_id
!= MAXVARS
; var
++)
2313 speakup_register_var(var
);
2314 for (var
= synth_time_vars
;
2315 (var
->var_id
>= 0) && (var
->var_id
< MAXVARS
); var
++)
2316 speakup_register_var(var
);
2317 for (i
= 1; spk_punc_info
[i
].mask
!= 0; i
++)
2318 spk_set_mask_bits(NULL
, i
, 2);
2320 spk_set_key_info(spk_key_defaults
, spk_key_buf
);
2322 /* From here on out, initializations can fail. */
2323 err
= speakup_add_virtual_keyboard();
2325 goto error_virtkeyboard
;
2327 first_console
= kzalloc(sizeof(*first_console
), GFP_KERNEL
);
2328 if (!first_console
) {
2333 speakup_console
[vc
->vc_num
] = first_console
;
2336 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2338 err
= speakup_allocate(vc_cons
[i
].d
);
2340 goto error_kobjects
;
2344 spk_shut_up
|= 0x01;
2346 err
= speakup_kobj_init();
2348 goto error_kobjects
;
2350 synth_init(synth_name
);
2351 speakup_register_devsynth();
2353 * register_devsynth might fail, but this error is not fatal.
2354 * /dev/synth is an extra feature; the rest of Speakup
2355 * will work fine without it.
2358 err
= register_keyboard_notifier(&keyboard_notifier_block
);
2360 goto error_kbdnotifier
;
2361 err
= register_vt_notifier(&vt_notifier_block
);
2363 goto error_vtnotifier
;
2365 speakup_task
= kthread_create(speakup_thread
, NULL
, "speakup");
2367 if (IS_ERR(speakup_task
)) {
2368 err
= PTR_ERR(speakup_task
);
2372 set_user_nice(speakup_task
, 10);
2373 wake_up_process(speakup_task
);
2375 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION
);
2376 pr_info("synth name on entry is: %s\n", synth_name
);
2380 unregister_vt_notifier(&vt_notifier_block
);
2383 unregister_keyboard_notifier(&keyboard_notifier_block
);
2384 del_timer(&cursor_timer
);
2387 speakup_unregister_devsynth();
2388 mutex_lock(&spk_mutex
);
2390 mutex_unlock(&spk_mutex
);
2391 speakup_kobj_exit();
2394 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2395 kfree(speakup_console
[i
]);
2398 speakup_remove_virtual_keyboard();
2401 for (i
= 0; i
< MAXVARS
; i
++)
2402 speakup_unregister_var(i
);
2404 for (i
= 0; i
< 256; i
++) {
2405 if (spk_characters
[i
] != spk_default_chars
[i
])
2406 kfree(spk_characters
[i
]);
2409 spk_free_user_msgs();
2415 module_init(speakup_init
);
2416 module_exit(speakup_exit
);