blob: 63c59bc89b040fe68fcf01d364d5b636e8ac6707 [file] [log] [blame]
William Hubbsc6e3fd22010-10-07 13:20:02 -05001/* speakup.c
William Hubbs16d35512010-10-15 22:13:34 -05002 * review functions for the speakup screen review package.
3 * originally written by: Kirk Reiser and Andy Berdan.
4 *
5 * extensively modified by David Borowski.
6 *
7 ** Copyright (C) 1998 Kirk Reiser.
8 * Copyright (C) 2003 David Borowski.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
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
William Hubbsc6e3fd22010-10-07 13:20:02 -050023*/
24
25#include <linux/kernel.h>
William Hubbsc6e3fd22010-10-07 13:20:02 -050026#include <linux/vt.h>
27#include <linux/tty.h>
William Hubbs16d35512010-10-15 22:13:34 -050028#include <linux/mm.h> /* __get_free_page() and friends */
William Hubbsc6e3fd22010-10-07 13:20:02 -050029#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 */
William Hubbs16d35512010-10-15 22:13:34 -050036#include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
William Hubbsc6e3fd22010-10-07 13:20:02 -050037#include <linux/input.h>
38#include <linux/kmod.h>
39
William Hubbsc6e3fd22010-10-07 13:20:02 -050040/* 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>
46
47#include <linux/spinlock.h>
48#include <linux/notifier.h>
49
William Hubbs16d35512010-10-15 22:13:34 -050050#include <linux/uaccess.h> /* copy_from|to|user() and others */
William Hubbsc6e3fd22010-10-07 13:20:02 -050051
52#include "spk_priv.h"
53#include "speakup.h"
54
55#define MAX_DELAY msecs_to_jiffies(500)
56#define MINECHOCHAR SPACE
57
58MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
59MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
60MODULE_DESCRIPTION("Speakup console speech");
61MODULE_LICENSE("GPL");
62MODULE_VERSION(SPEAKUP_VERSION);
63
64char *synth_name;
65module_param_named(synth, synth_name, charp, S_IRUGO);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +010066module_param_named(quiet, spk_quiet_boot, bool, S_IRUGO);
William Hubbsc6e3fd22010-10-07 13:20:02 -050067
68MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
69MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
70
Samuel Thibaultca2beaf2013-01-02 02:37:40 +010071special_func spk_special_handler;
William Hubbsc6e3fd22010-10-07 13:20:02 -050072
Samuel Thibaultca2beaf2013-01-02 02:37:40 +010073short spk_pitch_shift, synth_flags;
William Hubbsc6e3fd22010-10-07 13:20:02 -050074static char buf[256];
Samuel Thibaultca2beaf2013-01-02 02:37:40 +010075int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
76int spk_no_intr, spk_spell_delay;
77int spk_key_echo, spk_say_word_ctl;
78int spk_say_ctrl, spk_bell_pos;
79short spk_punc_mask;
80int spk_punc_level, spk_reading_punc;
Domagoj Trsane7027b92014-09-09 20:04:31 +020081char spk_str_caps_start[MAXVARLEN + 1] = "\0";
82char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
Samuel Thibaultca2beaf2013-01-02 02:37:40 +010083const struct st_bits_data spk_punc_info[] = {
William Hubbs16d35512010-10-15 22:13:34 -050084 {"none", "", 0},
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},
Shalin Mehtaab06e0f2013-10-03 01:08:42 -070092 {NULL, NULL}
William Hubbsc6e3fd22010-10-07 13:20:02 -050093};
William Hubbs16d35512010-10-15 22:13:34 -050094
William Hubbsc6e3fd22010-10-07 13:20:02 -050095static char mark_cut_flag;
96#define MAX_KEY 160
Sachin Kamat00121962013-05-22 14:37:25 +053097static u_char *spk_shift_table;
98u_char *spk_our_keys[MAX_KEY];
Samuel Thibaultca2beaf2013-01-02 02:37:40 +010099u_char spk_key_buf[600];
100const u_char spk_key_defaults[] = {
William Hubbsc6e3fd22010-10-07 13:20:02 -0500101#include "speakupmap.h"
102};
103
104/* Speakup Cursor Track Variables */
105static int cursor_track = 1, prev_cursor_track = 1;
106
107/* cursor track modes, must be ordered same as cursor_msgs */
108enum {
109 CT_Off = 0,
110 CT_On,
111 CT_Highlight,
112 CT_Window,
113 CT_Max
114};
115#define read_all_mode CT_Max
116
117static struct tty_struct *tty;
118
119static void spkup_write(const char *in_buf, int count);
120
William Hubbsc6e3fd22010-10-07 13:20:02 -0500121static char *phonetic[] = {
122 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
William Hubbs16d35512010-10-15 22:13:34 -0500123 "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
124 "papa",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500125 "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
126 "x ray", "yankee", "zulu"
127};
128
129/* array of 256 char pointers (one for each character description)
130 * initialized to default_chars and user selectable via
Aleksei Fedotov13d825e2015-08-14 22:34:37 +0300131 * /proc/speakup/characters
132 */
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100133char *spk_characters[256];
William Hubbsc6e3fd22010-10-07 13:20:02 -0500134
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100135char *spk_default_chars[256] = {
William Hubbs16d35512010-10-15 22:13:34 -0500136/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500137/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
138/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
William Hubbs16d35512010-10-15 22:13:34 -0500139/*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
140 "control",
141/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
142 "tick",
143/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
144 "dot",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500145 "slash",
146/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
147 "eight", "nine",
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",
William Hubbs16d35512010-10-15 22:13:34 -0500152/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
153 "caret",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500154 "line",
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",
William Hubbs16d35512010-10-15 22:13:34 -0500159/*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",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500166/*160*/ "nbsp", "inverted bang",
William Hubbs16d35512010-10-15 22:13:34 -0500167/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
168/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500169/*172*/ "not", "soft hyphen", "registered", "macron",
William Hubbs16d35512010-10-15 22:13:34 -0500170/*176*/ "degrees", "plus or minus", "super two", "super three",
171/*180*/ "acute accent", "micro", "pilcrow", "middle dot",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500172/*184*/ "cedilla", "super one", "male ordinal", "double right angle",
William Hubbs16d35512010-10-15 22:13:34 -0500173/*188*/ "one quarter", "one half", "three quarters",
174 "inverted question",
175/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
176 "A RING",
177/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
178 "E OOMLAUT",
179/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
180 "N TILDE",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500181/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
William Hubbs16d35512010-10-15 22:13:34 -0500182/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
183 "U CIRCUMFLEX",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500184/*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",
William Hubbs16d35512010-10-15 22:13:34 -0500187/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
188 "i circumflex",
189/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
190 "o circumflex",
191/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
192 "u acute",
William Hubbsc6e3fd22010-10-07 13:20:02 -0500193/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
194};
195
196/* array of 256 u_short (one for each character)
197 * initialized to default_chartab and user selectable via
Aleksei Fedotov13d825e2015-08-14 22:34:37 +0300198 * /sys/module/speakup/parameters/chartab
199 */
William Hubbsc6e3fd22010-10-07 13:20:02 -0500200u_short spk_chartab[256];
201
202static u_short default_chartab[256] = {
William Hubbs16d35512010-10-15 22:13:34 -0500203 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 */
220 B_SYM, /* 135 */
221 B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
222 B_CAPSYM, /* 143 */
223 B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
224 B_SYM, /* 151 */
225 B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
226 B_SYM, /* 159 */
227 WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
228 B_SYM, /* 167 */
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 */
William Hubbsc6e3fd22010-10-07 13:20:02 -0500240};
241
242struct task_struct *speakup_task;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100243struct bleep spk_unprocessed_sound;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500244static int spk_keydown;
245static u_char spk_lastkey, spk_close_press, keymap_flags;
246static u_char last_keycode, this_speakup_key;
247static u_long last_spk_jiffy;
248
249struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
250
251DEFINE_MUTEX(spk_mutex);
252
253static int keyboard_notifier_call(struct notifier_block *,
254 unsigned long code, void *param);
255
Samuel Thibaultd9f54202013-01-02 02:36:56 +0100256static struct notifier_block keyboard_notifier_block = {
William Hubbsc6e3fd22010-10-07 13:20:02 -0500257 .notifier_call = keyboard_notifier_call,
258};
259
260static int vt_notifier_call(struct notifier_block *,
261 unsigned long code, void *param);
262
Samuel Thibaultd9f54202013-01-02 02:36:56 +0100263static struct notifier_block vt_notifier_block = {
William Hubbsc6e3fd22010-10-07 13:20:02 -0500264 .notifier_call = vt_notifier_call,
265};
266
267static unsigned char get_attributes(u16 *pos)
268{
William Hubbs16d35512010-10-15 22:13:34 -0500269 return (u_char) (scr_readw(pos) >> 8);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500270}
271
272static void speakup_date(struct vc_data *vc)
273{
274 spk_x = spk_cx = vc->vc_x;
275 spk_y = spk_cy = vc->vc_y;
276 spk_pos = spk_cp = vc->vc_pos;
277 spk_old_attr = spk_attr;
278 spk_attr = get_attributes((u_short *) spk_pos);
279}
280
281static void bleep(u_short val)
282{
283 static const short vals[] = {
284 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
285 };
286 short freq;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100287 int time = spk_bleep_time;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200288
William Hubbs16d35512010-10-15 22:13:34 -0500289 freq = vals[val % 12];
William Hubbsc6e3fd22010-10-07 13:20:02 -0500290 if (val > 11)
William Hubbs16d35512010-10-15 22:13:34 -0500291 freq *= (1 << (val / 12));
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100292 spk_unprocessed_sound.freq = freq;
293 spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
294 spk_unprocessed_sound.active = 1;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500295 /* We can only have 1 active sound at a time. */
296}
297
298static void speakup_shut_up(struct vc_data *vc)
299{
300 if (spk_killed)
301 return;
302 spk_shut_up |= 0x01;
303 spk_parked &= 0xfe;
304 speakup_date(vc);
305 if (synth != NULL)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100306 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -0500307}
308
309static void speech_kill(struct vc_data *vc)
310{
311 char val = synth->is_alive(synth);
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200312
William Hubbsc6e3fd22010-10-07 13:20:02 -0500313 if (val == 0)
314 return;
315
316 /* re-enables synth, if disabled */
317 if (val == 2 || spk_killed) {
318 /* dead */
319 spk_shut_up &= ~0x40;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100320 synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500321 } else {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100322 synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500323 spk_shut_up |= 0x40;
324 }
325}
326
327static void speakup_off(struct vc_data *vc)
328{
329 if (spk_shut_up & 0x80) {
330 spk_shut_up &= 0x7f;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100331 synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500332 } else {
333 spk_shut_up |= 0x80;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100334 synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500335 }
336 speakup_date(vc);
337}
338
339static void speakup_parked(struct vc_data *vc)
340{
341 if (spk_parked & 0x80) {
342 spk_parked = 0;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100343 synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500344 } else {
345 spk_parked |= 0x80;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100346 synth_printf("%s\n", spk_msg_get(MSG_PARKED));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500347 }
348}
349
350static void speakup_cut(struct vc_data *vc)
351{
352 static const char err_buf[] = "set selection failed";
353 int ret;
354
355 if (!mark_cut_flag) {
356 mark_cut_flag = 1;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100357 spk_xs = (u_short) spk_x;
358 spk_ys = (u_short) spk_y;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500359 spk_sel_cons = vc;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100360 synth_printf("%s\n", spk_msg_get(MSG_MARK));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500361 return;
362 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100363 spk_xe = (u_short) spk_x;
364 spk_ye = (u_short) spk_y;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500365 mark_cut_flag = 0;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100366 synth_printf("%s\n", spk_msg_get(MSG_CUT));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500367
368 speakup_clear_selection();
369 ret = speakup_set_selection(tty);
370
371 switch (ret) {
372 case 0:
William Hubbs16d35512010-10-15 22:13:34 -0500373 break; /* no error */
374 case -EFAULT:
William Hubbsc6e3fd22010-10-07 13:20:02 -0500375 pr_warn("%sEFAULT\n", err_buf);
376 break;
William Hubbs16d35512010-10-15 22:13:34 -0500377 case -EINVAL:
William Hubbsc6e3fd22010-10-07 13:20:02 -0500378 pr_warn("%sEINVAL\n", err_buf);
379 break;
William Hubbs16d35512010-10-15 22:13:34 -0500380 case -ENOMEM:
William Hubbsc6e3fd22010-10-07 13:20:02 -0500381 pr_warn("%sENOMEM\n", err_buf);
382 break;
383 }
384}
385
386static void speakup_paste(struct vc_data *vc)
387{
388 if (mark_cut_flag) {
389 mark_cut_flag = 0;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100390 synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500391 } else {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100392 synth_printf("%s\n", spk_msg_get(MSG_PASTE));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500393 speakup_paste_selection(tty);
394 }
395}
396
397static void say_attributes(struct vc_data *vc)
398{
399 int fg = spk_attr & 0x0f;
400 int bg = spk_attr >> 4;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200401
William Hubbsc6e3fd22010-10-07 13:20:02 -0500402 if (fg > 8) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100403 synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500404 fg -= 8;
405 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100406 synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500407 if (bg > 7) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100408 synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500409 bg -= 8;
410 } else
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100411 synth_printf(" %s ", spk_msg_get(MSG_ON));
412 synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500413}
414
415enum {
416 edge_top = 1,
417 edge_bottom,
418 edge_left,
419 edge_right,
420 edge_quiet
421};
422
423static void announce_edge(struct vc_data *vc, int msg_id)
424{
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100425 if (spk_bleeps & 1)
William Hubbsc6e3fd22010-10-07 13:20:02 -0500426 bleep(spk_y);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100427 if ((spk_bleeps & 2) && (msg_id < edge_quiet))
Shirish Gajera63b8ebe2015-03-28 13:21:39 -0700428 synth_printf("%s\n",
429 spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500430}
431
432static void speak_char(u_char ch)
433{
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100434 char *cp = spk_characters[ch];
435 struct var_t *direct = spk_get_var(DIRECT);
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200436
William Hubbsc6e3fd22010-10-07 13:20:02 -0500437 if (direct && direct->u.n.value) {
438 if (IS_CHAR(ch, B_CAP)) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100439 spk_pitch_shift++;
440 synth_printf("%s", spk_str_caps_start);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500441 }
442 synth_printf("%c", ch);
443 if (IS_CHAR(ch, B_CAP))
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100444 synth_printf("%s", spk_str_caps_stop);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500445 return;
446 }
447 if (cp == NULL) {
448 pr_info("speak_char: cp == NULL!\n");
449 return;
450 }
451 synth_buffer_add(SPACE);
452 if (IS_CHAR(ch, B_CAP)) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100453 spk_pitch_shift++;
454 synth_printf("%s", spk_str_caps_start);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500455 synth_printf("%s", cp);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100456 synth_printf("%s", spk_str_caps_stop);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500457 } else {
458 if (*cp == '^') {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100459 synth_printf("%s", spk_msg_get(MSG_CTRL));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500460 cp++;
461 }
462 synth_printf("%s", cp);
463 }
464 synth_buffer_add(SPACE);
465}
466
Lisa Nguyen69d8ba52013-05-17 11:51:18 -0700467static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
William Hubbsc6e3fd22010-10-07 13:20:02 -0500468{
469 u16 ch = ' ';
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200470
William Hubbsc6e3fd22010-10-07 13:20:02 -0500471 if (vc && pos) {
472 u16 w = scr_readw(pos);
473 u16 c = w & 0xff;
474
475 if (w & vc->vc_hi_font_mask)
476 c |= 0x100;
477
478 ch = inverse_translate(vc, c, 0);
479 *attribs = (w & 0xff00) >> 8;
480 }
481 return ch;
482}
483
484static void say_char(struct vc_data *vc)
485{
486 u_short ch;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200487
William Hubbsc6e3fd22010-10-07 13:20:02 -0500488 spk_old_attr = spk_attr;
489 ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
490 if (spk_attr != spk_old_attr) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100491 if (spk_attrib_bleep & 1)
William Hubbsc6e3fd22010-10-07 13:20:02 -0500492 bleep(spk_y);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100493 if (spk_attrib_bleep & 2)
William Hubbsc6e3fd22010-10-07 13:20:02 -0500494 say_attributes(vc);
495 }
496 speak_char(ch & 0xff);
497}
498
499static void say_phonetic_char(struct vc_data *vc)
500{
501 u_short ch;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200502
William Hubbsc6e3fd22010-10-07 13:20:02 -0500503 spk_old_attr = spk_attr;
504 ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
505 if (isascii(ch) && isalpha(ch)) {
506 ch &= 0x1f;
507 synth_printf("%s\n", phonetic[--ch]);
508 } else {
509 if (IS_CHAR(ch, B_NUM))
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100510 synth_printf("%s ", spk_msg_get(MSG_NUMBER));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500511 speak_char(ch);
512 }
513}
514
515static void say_prev_char(struct vc_data *vc)
516{
517 spk_parked |= 0x01;
518 if (spk_x == 0) {
519 announce_edge(vc, edge_left);
520 return;
521 }
522 spk_x--;
523 spk_pos -= 2;
524 say_char(vc);
525}
526
527static void say_next_char(struct vc_data *vc)
528{
529 spk_parked |= 0x01;
530 if (spk_x == vc->vc_cols - 1) {
531 announce_edge(vc, edge_right);
532 return;
533 }
534 spk_x++;
535 spk_pos += 2;
536 say_char(vc);
537}
538
539/* get_word - will first check to see if the character under the
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100540 * reading cursor is a space and if spk_say_word_ctl is true it will
541 * return the word space. If spk_say_word_ctl is not set it will check to
William Hubbs16d35512010-10-15 22:13:34 -0500542 * see if there is a word starting on the next position to the right
543 * and return that word if it exists. If it does not exist it will
544 * move left to the beginning of any previous word on the line or the
Aleksei Fedotov13d825e2015-08-14 22:34:37 +0300545 * beginning off the line whichever comes first..
546 */
William Hubbsc6e3fd22010-10-07 13:20:02 -0500547
548static u_long get_word(struct vc_data *vc)
549{
550 u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
551 char ch;
552 u_short attr_ch;
553 u_char temp;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200554
William Hubbsc6e3fd22010-10-07 13:20:02 -0500555 spk_old_attr = spk_attr;
William Hubbs16d35512010-10-15 22:13:34 -0500556 ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500557
558/* decided to take out the sayword if on a space (mis-information */
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100559 if (spk_say_word_ctl && ch == SPACE) {
William Hubbsc6e3fd22010-10-07 13:20:02 -0500560 *buf = '\0';
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100561 synth_printf("%s\n", spk_msg_get(MSG_SPACE));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500562 return 0;
563 } else if ((tmpx < vc->vc_cols - 2)
564 && (ch == SPACE || ch == 0 || IS_WDLM(ch))
William Hubbs16d35512010-10-15 22:13:34 -0500565 && ((char)get_char(vc, (u_short *) &tmp_pos + 1, &temp) >
566 SPACE)) {
William Hubbsc6e3fd22010-10-07 13:20:02 -0500567 tmp_pos += 2;
568 tmpx++;
569 } else
570 while (tmpx > 0) {
William Hubbs16d35512010-10-15 22:13:34 -0500571 ch = (char)get_char(vc, (u_short *) tmp_pos - 1, &temp);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500572 if ((ch == SPACE || ch == 0 || IS_WDLM(ch))
William Hubbs16d35512010-10-15 22:13:34 -0500573 && ((char)get_char(vc, (u_short *) tmp_pos, &temp) >
574 SPACE))
William Hubbsc6e3fd22010-10-07 13:20:02 -0500575 break;
576 tmp_pos -= 2;
577 tmpx--;
578 }
579 attr_ch = get_char(vc, (u_short *) tmp_pos, &spk_attr);
580 buf[cnt++] = attr_ch & 0xff;
581 while (tmpx < vc->vc_cols - 1) {
582 tmp_pos += 2;
583 tmpx++;
William Hubbs16d35512010-10-15 22:13:34 -0500584 ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
585 if ((ch == SPACE) || ch == 0
586 || (IS_WDLM(buf[cnt - 1]) && (ch > SPACE)))
William Hubbsc6e3fd22010-10-07 13:20:02 -0500587 break;
588 buf[cnt++] = ch;
589 }
590 buf[cnt] = '\0';
591 return cnt;
592}
593
594static void say_word(struct vc_data *vc)
595{
596 u_long cnt = get_word(vc);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100597 u_short saved_punc_mask = spk_punc_mask;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200598
William Hubbsc6e3fd22010-10-07 13:20:02 -0500599 if (cnt == 0)
600 return;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100601 spk_punc_mask = PUNC;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500602 buf[cnt++] = SPACE;
603 spkup_write(buf, cnt);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100604 spk_punc_mask = saved_punc_mask;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500605}
606
607static void say_prev_word(struct vc_data *vc)
608{
609 u_char temp;
610 char ch;
611 u_short edge_said = 0, last_state = 0, state = 0;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200612
William Hubbsc6e3fd22010-10-07 13:20:02 -0500613 spk_parked |= 0x01;
614
615 if (spk_x == 0) {
616 if (spk_y == 0) {
617 announce_edge(vc, edge_top);
618 return;
619 }
620 spk_y--;
621 spk_x = vc->vc_cols;
622 edge_said = edge_quiet;
623 }
624 while (1) {
625 if (spk_x == 0) {
626 if (spk_y == 0) {
627 edge_said = edge_top;
628 break;
629 }
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--;
William Hubbs16d35512010-10-15 22:13:34 -0500638 spk_pos -= 2;
639 ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500640 if (ch == SPACE || ch == 0)
641 state = 0;
642 else if (IS_WDLM(ch))
643 state = 1;
644 else
645 state = 2;
646 if (state < last_state) {
647 spk_pos += 2;
648 spk_x++;
649 break;
650 }
651 last_state = state;
652 }
653 if (spk_x == 0 && edge_said == edge_quiet)
654 edge_said = edge_left;
655 if (edge_said > 0 && edge_said < edge_quiet)
656 announce_edge(vc, edge_said);
657 say_word(vc);
658}
659
660static void say_next_word(struct vc_data *vc)
661{
662 u_char temp;
663 char ch;
664 u_short edge_said = 0, last_state = 2, state = 0;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500665
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200666 spk_parked |= 0x01;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500667 if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
668 announce_edge(vc, edge_bottom);
669 return;
670 }
671 while (1) {
William Hubbs16d35512010-10-15 22:13:34 -0500672 ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500673 if (ch == SPACE || ch == 0)
674 state = 0;
675 else if (IS_WDLM(ch))
676 state = 1;
677 else
678 state = 2;
679 if (state > last_state)
680 break;
681 if (spk_x >= vc->vc_cols - 1) {
682 if (spk_y == vc->vc_rows - 1) {
683 edge_said = edge_bottom;
684 break;
685 }
686 state = 0;
687 spk_y++;
688 spk_x = 0;
689 edge_said = edge_right;
690 } else
691 spk_x++;
692 spk_pos += 2;
693 last_state = state;
694 }
695 if (edge_said > 0)
696 announce_edge(vc, edge_said);
697 say_word(vc);
698}
699
700static void spell_word(struct vc_data *vc)
701{
Dilek Uzulmez0f709482014-10-07 10:59:18 +0300702 static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100703 char *cp = buf, *str_cap = spk_str_caps_stop;
704 char *cp1, *last_cap = spk_str_caps_stop;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500705 u_char ch;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200706
William Hubbsc6e3fd22010-10-07 13:20:02 -0500707 if (!get_word(vc))
708 return;
709 while ((ch = (u_char) *cp)) {
710 if (cp != buf)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100711 synth_printf(" %s ", delay_str[spk_spell_delay]);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500712 if (IS_CHAR(ch, B_CAP)) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100713 str_cap = spk_str_caps_start;
714 if (*spk_str_caps_stop)
715 spk_pitch_shift++;
William Hubbs16d35512010-10-15 22:13:34 -0500716 else /* synth has no pitch */
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100717 last_cap = spk_str_caps_stop;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500718 } else
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100719 str_cap = spk_str_caps_stop;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500720 if (str_cap != last_cap) {
721 synth_printf("%s", str_cap);
722 last_cap = str_cap;
723 }
724 if (this_speakup_key == SPELL_PHONETIC
725 && (isascii(ch) && isalpha(ch))) {
726 ch &= 31;
727 cp1 = phonetic[--ch];
728 } else {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100729 cp1 = spk_characters[ch];
William Hubbsc6e3fd22010-10-07 13:20:02 -0500730 if (*cp1 == '^') {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100731 synth_printf("%s", spk_msg_get(MSG_CTRL));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500732 cp1++;
733 }
734 }
735 synth_printf("%s", cp1);
736 cp++;
737 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100738 if (str_cap != spk_str_caps_stop)
739 synth_printf("%s", spk_str_caps_stop);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500740}
741
742static int get_line(struct vc_data *vc)
743{
744 u_long tmp = spk_pos - (spk_x * 2);
745 int i = 0;
746 u_char tmp2;
747
748 spk_old_attr = spk_attr;
749 spk_attr = get_attributes((u_short *) spk_pos);
750 for (i = 0; i < vc->vc_cols; i++) {
751 buf[i] = (u_char) get_char(vc, (u_short *) tmp, &tmp2);
752 tmp += 2;
753 }
754 for (--i; i >= 0; i--)
755 if (buf[i] != SPACE)
756 break;
757 return ++i;
758}
759
760static void say_line(struct vc_data *vc)
761{
762 int i = get_line(vc);
763 char *cp;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100764 u_short saved_punc_mask = spk_punc_mask;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200765
William Hubbsc6e3fd22010-10-07 13:20:02 -0500766 if (i == 0) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100767 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500768 return;
769 }
770 buf[i++] = '\n';
771 if (this_speakup_key == SAY_LINE_INDENT) {
William Hubbs16d35512010-10-15 22:13:34 -0500772 cp = buf;
773 while (*cp == SPACE)
774 cp++;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500775 synth_printf("%d, ", (cp - buf) + 1);
776 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100777 spk_punc_mask = spk_punc_masks[spk_reading_punc];
William Hubbsc6e3fd22010-10-07 13:20:02 -0500778 spkup_write(buf, i);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100779 spk_punc_mask = saved_punc_mask;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500780}
781
782static void say_prev_line(struct vc_data *vc)
783{
784 spk_parked |= 0x01;
785 if (spk_y == 0) {
786 announce_edge(vc, edge_top);
787 return;
788 }
789 spk_y--;
790 spk_pos -= vc->vc_size_row;
791 say_line(vc);
792}
793
794static void say_next_line(struct vc_data *vc)
795{
796 spk_parked |= 0x01;
797 if (spk_y == vc->vc_rows - 1) {
798 announce_edge(vc, edge_bottom);
799 return;
800 }
801 spk_y++;
802 spk_pos += vc->vc_size_row;
803 say_line(vc);
804}
805
806static int say_from_to(struct vc_data *vc, u_long from, u_long to,
807 int read_punc)
808{
809 int i = 0;
810 u_char tmp;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100811 u_short saved_punc_mask = spk_punc_mask;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200812
William Hubbsc6e3fd22010-10-07 13:20:02 -0500813 spk_old_attr = spk_attr;
814 spk_attr = get_attributes((u_short *) from);
815 while (from < to) {
William Hubbs16d35512010-10-15 22:13:34 -0500816 buf[i++] = (char)get_char(vc, (u_short *) from, &tmp);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500817 from += 2;
818 if (i >= vc->vc_size_row)
819 break;
820 }
821 for (--i; i >= 0; i--)
822 if (buf[i] != SPACE)
823 break;
824 buf[++i] = SPACE;
825 buf[++i] = '\0';
826 if (i < 1)
827 return i;
828 if (read_punc)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100829 spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500830 spkup_write(buf, i);
831 if (read_punc)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100832 spk_punc_mask = saved_punc_mask;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500833 return i - 1;
834}
835
836static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
837 int read_punc)
838{
839 u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
840 u_long end = start + (to * 2);
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200841
William Hubbsc6e3fd22010-10-07 13:20:02 -0500842 start += from * 2;
843 if (say_from_to(vc, start, end, read_punc) <= 0)
844 if (cursor_track != read_all_mode)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100845 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500846}
847
848/* Sentence Reading Commands */
849
William Hubbsc6e3fd22010-10-07 13:20:02 -0500850static int currsentence;
851static int numsentences[2];
852static char *sentbufend[2];
853static char *sentmarks[2][10];
854static int currbuf;
855static int bn;
856static char sentbuf[2][256];
857
William Hubbs16d35512010-10-15 22:13:34 -0500858static int say_sentence_num(int num, int prev)
William Hubbsc6e3fd22010-10-07 13:20:02 -0500859{
860 bn = currbuf;
861 currsentence = num + 1;
862 if (prev && --bn == -1)
863 bn = 1;
864
865 if (num > numsentences[bn])
866 return 0;
867
868 spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
869 return 1;
870}
871
872static int get_sentence_buf(struct vc_data *vc, int read_punc)
873{
874 u_long start, end;
875 int i, bn;
876 u_char tmp;
877
878 currbuf++;
879 if (currbuf == 2)
880 currbuf = 0;
881 bn = currbuf;
882 start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
William Hubbs16d35512010-10-15 22:13:34 -0500883 end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
William Hubbsc6e3fd22010-10-07 13:20:02 -0500884
885 numsentences[bn] = 0;
886 sentmarks[bn][0] = &sentbuf[bn][0];
887 i = 0;
888 spk_old_attr = spk_attr;
889 spk_attr = get_attributes((u_short *) start);
890
891 while (start < end) {
William Hubbs16d35512010-10-15 22:13:34 -0500892 sentbuf[bn][i] = (char)get_char(vc, (u_short *) start, &tmp);
William Hubbsc6e3fd22010-10-07 13:20:02 -0500893 if (i > 0) {
William Hubbs16d35512010-10-15 22:13:34 -0500894 if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.'
William Hubbsc6e3fd22010-10-07 13:20:02 -0500895 && numsentences[bn] < 9) {
896 /* Sentence Marker */
897 numsentences[bn]++;
898 sentmarks[bn][numsentences[bn]] =
William Hubbs16d35512010-10-15 22:13:34 -0500899 &sentbuf[bn][i];
William Hubbsc6e3fd22010-10-07 13:20:02 -0500900 }
901 }
902 i++;
903 start += 2;
904 if (i >= vc->vc_size_row)
905 break;
906 }
907
908 for (--i; i >= 0; i--)
909 if (sentbuf[bn][i] != SPACE)
910 break;
911
912 if (i < 1)
913 return -1;
914
915 sentbuf[bn][++i] = SPACE;
916 sentbuf[bn][++i] = '\0';
917
918 sentbufend[bn] = &sentbuf[bn][i];
919 return numsentences[bn];
920}
921
922static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
923{
924 u_long start = vc->vc_origin, end;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200925
William Hubbsc6e3fd22010-10-07 13:20:02 -0500926 if (from > 0)
927 start += from * vc->vc_size_row;
928 if (to > vc->vc_rows)
929 to = vc->vc_rows;
930 end = vc->vc_origin + (to * vc->vc_size_row);
931 for (from = start; from < end; from = to) {
932 to = from + vc->vc_size_row;
933 say_from_to(vc, from, to, 1);
934 }
935}
936
937static void say_screen(struct vc_data *vc)
938{
939 say_screen_from_to(vc, 0, vc->vc_rows);
940}
941
942static void speakup_win_say(struct vc_data *vc)
943{
944 u_long start, end, from, to;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200945
William Hubbsc6e3fd22010-10-07 13:20:02 -0500946 if (win_start < 2) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100947 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
William Hubbsc6e3fd22010-10-07 13:20:02 -0500948 return;
949 }
950 start = vc->vc_origin + (win_top * vc->vc_size_row);
951 end = vc->vc_origin + (win_bottom * vc->vc_size_row);
952 while (start <= end) {
953 from = start + (win_left * 2);
954 to = start + (win_right * 2);
955 say_from_to(vc, from, to, 1);
956 start += vc->vc_size_row;
957 }
958}
959
960static void top_edge(struct vc_data *vc)
961{
962 spk_parked |= 0x01;
963 spk_pos = vc->vc_origin + 2 * spk_x;
964 spk_y = 0;
965 say_line(vc);
966}
967
968static void bottom_edge(struct vc_data *vc)
969{
970 spk_parked |= 0x01;
971 spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
972 spk_y = vc->vc_rows - 1;
973 say_line(vc);
974}
975
976static void left_edge(struct vc_data *vc)
977{
978 spk_parked |= 0x01;
979 spk_pos -= spk_x * 2;
980 spk_x = 0;
981 say_char(vc);
982}
983
984static void right_edge(struct vc_data *vc)
985{
986 spk_parked |= 0x01;
987 spk_pos += (vc->vc_cols - spk_x - 1) * 2;
988 spk_x = vc->vc_cols - 1;
989 say_char(vc);
990}
991
992static void say_first_char(struct vc_data *vc)
993{
994 int i, len = get_line(vc);
995 u_char ch;
Domagoj Trsan8e69a812014-09-09 20:04:34 +0200996
William Hubbsc6e3fd22010-10-07 13:20:02 -0500997 spk_parked |= 0x01;
998 if (len == 0) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +0100999 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001000 return;
1001 }
1002 for (i = 0; i < len; i++)
1003 if (buf[i] != SPACE)
1004 break;
1005 ch = buf[i];
1006 spk_pos -= (spk_x - i) * 2;
1007 spk_x = i;
1008 synth_printf("%d, ", ++i);
1009 speak_char(ch);
1010}
1011
1012static void say_last_char(struct vc_data *vc)
1013{
1014 int len = get_line(vc);
1015 u_char ch;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001016
William Hubbsc6e3fd22010-10-07 13:20:02 -05001017 spk_parked |= 0x01;
1018 if (len == 0) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001019 synth_printf("%s\n", spk_msg_get(MSG_BLANK));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001020 return;
1021 }
1022 ch = buf[--len];
1023 spk_pos -= (spk_x - len) * 2;
1024 spk_x = len;
1025 synth_printf("%d, ", ++len);
1026 speak_char(ch);
1027}
1028
1029static void say_position(struct vc_data *vc)
1030{
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001031 synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
William Hubbs16d35512010-10-15 22:13:34 -05001032 vc->vc_num + 1);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001033 synth_printf("\n");
1034}
1035
1036/* Added by brianb */
1037static void say_char_num(struct vc_data *vc)
1038{
1039 u_char tmp;
1040 u_short ch = get_char(vc, (u_short *) spk_pos, &tmp);
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001041
William Hubbsc6e3fd22010-10-07 13:20:02 -05001042 ch &= 0xff;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001043 synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001044}
1045
1046/* these are stub functions to keep keyboard.c happy. */
1047
1048static void say_from_top(struct vc_data *vc)
1049{
1050 say_screen_from_to(vc, 0, spk_y);
1051}
1052
1053static void say_to_bottom(struct vc_data *vc)
1054{
1055 say_screen_from_to(vc, spk_y, vc->vc_rows);
1056}
1057
1058static void say_from_left(struct vc_data *vc)
1059{
1060 say_line_from_to(vc, 0, spk_x, 1);
1061}
1062
1063static void say_to_right(struct vc_data *vc)
1064{
1065 say_line_from_to(vc, spk_x, vc->vc_cols, 1);
1066}
1067
1068/* end of stub functions. */
1069
1070static void spkup_write(const char *in_buf, int count)
1071{
William Hubbs16d35512010-10-15 22:13:34 -05001072 static int rep_count;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001073 static u_char ch = '\0', old_ch = '\0';
William Hubbs16d35512010-10-15 22:13:34 -05001074 static u_short char_type, last_type;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001075 int in_count = count;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001076
William Hubbsc6e3fd22010-10-07 13:20:02 -05001077 spk_keydown = 0;
1078 while (count--) {
1079 if (cursor_track == read_all_mode) {
1080 /* Insert Sentence Index */
1081 if ((in_buf == sentmarks[bn][currsentence]) &&
William Hubbs16d35512010-10-15 22:13:34 -05001082 (currsentence <= numsentences[bn]))
William Hubbsc6e3fd22010-10-07 13:20:02 -05001083 synth_insert_next_index(currsentence++);
1084 }
William Hubbs16d35512010-10-15 22:13:34 -05001085 ch = (u_char) *in_buf++;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001086 char_type = spk_chartab[ch];
William Hubbs16d35512010-10-15 22:13:34 -05001087 if (ch == old_ch && !(char_type & B_NUM)) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001088 if (++rep_count > 2)
1089 continue;
1090 } else {
William Hubbs16d35512010-10-15 22:13:34 -05001091 if ((last_type & CH_RPT) && rep_count > 2) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001092 synth_printf(" ");
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001093 synth_printf(spk_msg_get(MSG_REPEAT_DESC),
William Hubbs16d35512010-10-15 22:13:34 -05001094 ++rep_count);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001095 synth_printf(" ");
1096 }
1097 rep_count = 0;
1098 }
1099 if (ch == spk_lastkey) {
1100 rep_count = 0;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001101 if (spk_key_echo == 1 && ch >= MINECHOCHAR)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001102 speak_char(ch);
1103 } else if (char_type & B_ALPHA) {
1104 if ((synth_flags & SF_DEC) && (last_type & PUNC))
1105 synth_buffer_add(SPACE);
1106 synth_printf("%c", ch);
1107 } else if (char_type & B_NUM) {
1108 rep_count = 0;
1109 synth_printf("%c", ch);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001110 } else if (char_type & spk_punc_mask) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001111 speak_char(ch);
William Hubbs16d35512010-10-15 22:13:34 -05001112 char_type &= ~PUNC; /* for dec nospell processing */
1113 } else if (char_type & SYNTH_OK) {
1114 /* these are usually puncts like . and , which synth
1115 * needs for expression.
1116 * suppress multiple to get rid of long pauses and
1117 * clear repeat count
1118 * so if someone has
Aleksei Fedotov13d825e2015-08-14 22:34:37 +03001119 * repeats on you don't get nothing repeated count
1120 */
William Hubbsc6e3fd22010-10-07 13:20:02 -05001121 if (ch != old_ch)
1122 synth_printf("%c", ch);
1123 else
1124 rep_count = 0;
1125 } else {
1126/* send space and record position, if next is num overwrite space */
1127 if (old_ch != ch)
1128 synth_buffer_add(SPACE);
1129 else
1130 rep_count = 0;
1131 }
1132 old_ch = ch;
1133 last_type = char_type;
1134 }
1135 spk_lastkey = 0;
1136 if (in_count > 2 && rep_count > 2) {
William Hubbs16d35512010-10-15 22:13:34 -05001137 if (last_type & CH_RPT) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001138 synth_printf(" ");
Shirish Gajera63b8ebe2015-03-28 13:21:39 -07001139 synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
1140 ++rep_count);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001141 synth_printf(" ");
1142 }
1143 rep_count = 0;
1144 }
1145}
1146
1147static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
1148
1149static void read_all_doc(struct vc_data *vc);
1150static void cursor_done(u_long data);
1151static DEFINE_TIMER(cursor_timer, cursor_done, 0, 0);
1152
1153static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
1154{
1155 unsigned long flags;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001156
William Hubbsc6e3fd22010-10-07 13:20:02 -05001157 if (synth == NULL || up_flag || spk_killed)
1158 return;
William Hubbs3efe8102013-05-13 00:02:56 -05001159 spin_lock_irqsave(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001160 if (cursor_track == read_all_mode) {
1161 switch (value) {
1162 case KVAL(K_SHIFT):
1163 del_timer(&cursor_timer);
1164 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001165 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05001166 read_all_doc(vc);
1167 break;
1168 case KVAL(K_CTRL):
1169 del_timer(&cursor_timer);
1170 cursor_track = prev_cursor_track;
1171 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001172 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05001173 break;
1174 }
1175 } else {
1176 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001177 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05001178 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001179 if (spk_say_ctrl && value < NUM_CTL_LABELS)
1180 synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
William Hubbs3efe8102013-05-13 00:02:56 -05001181 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001182}
1183
1184static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
1185{
1186 unsigned long flags;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001187
William Hubbs3efe8102013-05-13 00:02:56 -05001188 spin_lock_irqsave(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001189 if (up_flag) {
1190 spk_lastkey = spk_keydown = 0;
William Hubbs3efe8102013-05-13 00:02:56 -05001191 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001192 return;
1193 }
1194 if (synth == NULL || spk_killed) {
William Hubbs3efe8102013-05-13 00:02:56 -05001195 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001196 return;
1197 }
1198 spk_shut_up &= 0xfe;
1199 spk_lastkey = value;
1200 spk_keydown++;
1201 spk_parked &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001202 if (spk_key_echo == 2 && value >= MINECHOCHAR)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001203 speak_char(value);
William Hubbs3efe8102013-05-13 00:02:56 -05001204 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001205}
1206
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001207int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001208{
1209 int i = 0, states, key_data_len;
1210 const u_char *cp = key_info;
1211 u_char *cp1 = k_buffer;
1212 u_char ch, version, num_keys;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001213
William Hubbsc6e3fd22010-10-07 13:20:02 -05001214 version = *cp++;
1215 if (version != KEY_MAP_VER)
1216 return -1;
1217 num_keys = *cp;
William Hubbs16d35512010-10-15 22:13:34 -05001218 states = (int)cp[1];
William Hubbsc6e3fd22010-10-07 13:20:02 -05001219 key_data_len = (states + 1) * (num_keys + 1);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001220 if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf))
William Hubbsc6e3fd22010-10-07 13:20:02 -05001221 return -2;
1222 memset(k_buffer, 0, SHIFT_TBL_SIZE);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001223 memset(spk_our_keys, 0, sizeof(spk_our_keys));
1224 spk_shift_table = k_buffer;
1225 spk_our_keys[0] = spk_shift_table;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001226 cp1 += SHIFT_TBL_SIZE;
1227 memcpy(cp1, cp, key_data_len + 3);
William Hubbs16d35512010-10-15 22:13:34 -05001228 /* get num_keys, states and data */
1229 cp1 += 2; /* now pointing at shift states */
William Hubbsc6e3fd22010-10-07 13:20:02 -05001230 for (i = 1; i <= states; i++) {
1231 ch = *cp1++;
1232 if (ch >= SHIFT_TBL_SIZE)
1233 return -3;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001234 spk_shift_table[ch] = i;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001235 }
1236 keymap_flags = *cp1++;
1237 while ((ch = *cp1)) {
1238 if (ch >= MAX_KEY)
1239 return -4;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001240 spk_our_keys[ch] = cp1;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001241 cp1 += states + 1;
1242 }
1243 return 0;
1244}
1245
1246static struct var_t spk_vars[] = {
1247 /* bell must be first to set high limit */
William Hubbs16d35512010-10-15 22:13:34 -05001248 {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
1249 {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
1250 {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
1251 {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
1252 {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
1253 {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1254 {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
1255 {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
1256 {SAY_CONTROL, TOGGLE_0},
1257 {SAY_WORD_CTL, TOGGLE_0},
1258 {NO_INTERRUPT, TOGGLE_0},
1259 {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
William Hubbsc6e3fd22010-10-07 13:20:02 -05001260 V_LAST_VAR
1261};
1262
William Hubbsc6e3fd22010-10-07 13:20:02 -05001263static void toggle_cursoring(struct vc_data *vc)
1264{
1265 if (cursor_track == read_all_mode)
1266 cursor_track = prev_cursor_track;
1267 if (++cursor_track >= CT_Max)
1268 cursor_track = 0;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001269 synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001270}
1271
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001272void spk_reset_default_chars(void)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001273{
1274 int i;
1275
1276 /* First, free any non-default */
1277 for (i = 0; i < 256; i++) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001278 if ((spk_characters[i] != NULL)
1279 && (spk_characters[i] != spk_default_chars[i]))
1280 kfree(spk_characters[i]);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001281 }
1282
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001283 memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001284}
1285
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001286void spk_reset_default_chartab(void)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001287{
1288 memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
1289}
1290
William Hubbs16d35512010-10-15 22:13:34 -05001291static const struct st_bits_data *pb_edit;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001292
1293static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
1294{
1295 short mask = pb_edit->mask, ch_type = spk_chartab[ch];
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001296
William Hubbs16d35512010-10-15 22:13:34 -05001297 if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001298 return -1;
1299 if (ch == SPACE) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001300 synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
1301 spk_special_handler = NULL;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001302 return 1;
1303 }
William Hubbs16d35512010-10-15 22:13:34 -05001304 if (mask < PUNC && !(ch_type & PUNC))
William Hubbsc6e3fd22010-10-07 13:20:02 -05001305 return -1;
1306 spk_chartab[ch] ^= mask;
1307 speak_char(ch);
1308 synth_printf(" %s\n",
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001309 (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
1310 spk_msg_get(MSG_OFF));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001311 return 1;
1312}
1313
1314/* Allocation concurrency is protected by the console semaphore */
Sachin Kamat00121962013-05-22 14:37:25 +05301315static int speakup_allocate(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001316{
1317 int vc_num;
1318
1319 vc_num = vc->vc_num;
1320 if (speakup_console[vc_num] == NULL) {
1321 speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
William Hubbs16d35512010-10-15 22:13:34 -05001322 GFP_ATOMIC);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001323 if (speakup_console[vc_num] == NULL)
Christopher Brannon628f3422010-12-19 22:50:24 +00001324 return -ENOMEM;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001325 speakup_date(vc);
1326 } else if (!spk_parked)
1327 speakup_date(vc);
Christopher Brannon628f3422010-12-19 22:50:24 +00001328
1329 return 0;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001330}
1331
Sachin Kamat00121962013-05-22 14:37:25 +05301332static void speakup_deallocate(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001333{
1334 int vc_num;
1335
1336 vc_num = vc->vc_num;
Ilia Mirkin39dd3e52011-03-13 00:29:10 -05001337 kfree(speakup_console[vc_num]);
1338 speakup_console[vc_num] = NULL;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001339}
1340
1341static u_char is_cursor;
1342static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
1343static int cursor_con;
1344
1345static void reset_highlight_buffers(struct vc_data *);
1346
1347static int read_all_key;
1348
William Hubbsc6e3fd22010-10-07 13:20:02 -05001349static void start_read_all_timer(struct vc_data *vc, int command);
1350
1351enum {
1352 RA_NOTHING,
1353 RA_NEXT_SENT,
1354 RA_PREV_LINE,
1355 RA_NEXT_LINE,
1356 RA_PREV_SENT,
1357 RA_DOWN_ARROW,
1358 RA_TIMER,
1359 RA_FIND_NEXT_SENT,
1360 RA_FIND_PREV_SENT,
1361};
1362
William Hubbs16d35512010-10-15 22:13:34 -05001363static void kbd_fakekey2(struct vc_data *vc, int command)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001364{
1365 del_timer(&cursor_timer);
1366 speakup_fake_down_arrow();
1367 start_read_all_timer(vc, command);
1368}
1369
William Hubbs16d35512010-10-15 22:13:34 -05001370static void read_all_doc(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001371{
1372 if ((vc->vc_num != fg_console) || synth == NULL || spk_shut_up)
1373 return;
1374 if (!synth_supports_indexing())
1375 return;
1376 if (cursor_track != read_all_mode)
1377 prev_cursor_track = cursor_track;
1378 cursor_track = read_all_mode;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001379 spk_reset_index_count(0);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001380 if (get_sentence_buf(vc, 0) == -1)
1381 kbd_fakekey2(vc, RA_DOWN_ARROW);
1382 else {
1383 say_sentence_num(0, 0);
1384 synth_insert_next_index(0);
1385 start_read_all_timer(vc, RA_TIMER);
1386 }
1387}
1388
William Hubbs16d35512010-10-15 22:13:34 -05001389static void stop_read_all(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001390{
1391 del_timer(&cursor_timer);
1392 cursor_track = prev_cursor_track;
1393 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001394 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05001395}
1396
William Hubbs16d35512010-10-15 22:13:34 -05001397static void start_read_all_timer(struct vc_data *vc, int command)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001398{
1399 struct var_t *cursor_timeout;
1400
1401 cursor_con = vc->vc_num;
1402 read_all_key = command;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001403 cursor_timeout = spk_get_var(CURSOR_TIME);
William Hubbs16d35512010-10-15 22:13:34 -05001404 mod_timer(&cursor_timer,
1405 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001406}
1407
William Hubbs16d35512010-10-15 22:13:34 -05001408static void handle_cursor_read_all(struct vc_data *vc, int command)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001409{
1410 int indcount, sentcount, rv, sn;
1411
1412 switch (command) {
1413 case RA_NEXT_SENT:
1414 /* Get Current Sentence */
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001415 spk_get_index_count(&indcount, &sentcount);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001416 /*printk("%d %d ", indcount, sentcount); */
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001417 spk_reset_index_count(sentcount + 1);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001418 if (indcount == 1) {
William Hubbs16d35512010-10-15 22:13:34 -05001419 if (!say_sentence_num(sentcount + 1, 0)) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001420 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1421 return;
1422 }
1423 synth_insert_next_index(0);
1424 } else {
1425 sn = 0;
William Hubbs16d35512010-10-15 22:13:34 -05001426 if (!say_sentence_num(sentcount + 1, 1)) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001427 sn = 1;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001428 spk_reset_index_count(sn);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001429 } else
1430 synth_insert_next_index(0);
1431 if (!say_sentence_num(sn, 0)) {
1432 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1433 return;
1434 }
1435 synth_insert_next_index(0);
1436 }
1437 start_read_all_timer(vc, RA_TIMER);
1438 break;
1439 case RA_PREV_SENT:
1440 break;
1441 case RA_NEXT_LINE:
1442 read_all_doc(vc);
1443 break;
1444 case RA_PREV_LINE:
1445 break;
1446 case RA_DOWN_ARROW:
1447 if (get_sentence_buf(vc, 0) == -1) {
1448 kbd_fakekey2(vc, RA_DOWN_ARROW);
1449 } else {
1450 say_sentence_num(0, 0);
1451 synth_insert_next_index(0);
1452 start_read_all_timer(vc, RA_TIMER);
1453 }
1454 break;
1455 case RA_FIND_NEXT_SENT:
1456 rv = get_sentence_buf(vc, 0);
1457 if (rv == -1)
1458 read_all_doc(vc);
1459 if (rv == 0)
1460 kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
1461 else {
1462 say_sentence_num(1, 0);
1463 synth_insert_next_index(0);
1464 start_read_all_timer(vc, RA_TIMER);
1465 }
1466 break;
1467 case RA_FIND_PREV_SENT:
1468 break;
1469 case RA_TIMER:
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001470 spk_get_index_count(&indcount, &sentcount);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001471 if (indcount < 2)
1472 kbd_fakekey2(vc, RA_DOWN_ARROW);
1473 else
1474 start_read_all_timer(vc, RA_TIMER);
1475 break;
1476 }
1477}
1478
1479static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1480{
1481 unsigned long flags;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001482
William Hubbs3efe8102013-05-13 00:02:56 -05001483 spin_lock_irqsave(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001484 if (cursor_track == read_all_mode) {
1485 spk_parked &= 0xfe;
1486 if (synth == NULL || up_flag || spk_shut_up) {
William Hubbs3efe8102013-05-13 00:02:56 -05001487 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001488 return NOTIFY_STOP;
1489 }
1490 del_timer(&cursor_timer);
1491 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001492 spk_do_flush();
William Hubbs16d35512010-10-15 22:13:34 -05001493 start_read_all_timer(vc, value + 1);
William Hubbs3efe8102013-05-13 00:02:56 -05001494 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001495 return NOTIFY_STOP;
1496 }
William Hubbs3efe8102013-05-13 00:02:56 -05001497 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001498 return NOTIFY_OK;
1499}
1500
1501static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
1502{
1503 unsigned long flags;
1504 struct var_t *cursor_timeout;
1505
William Hubbs3efe8102013-05-13 00:02:56 -05001506 spin_lock_irqsave(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001507 spk_parked &= 0xfe;
1508 if (synth == NULL || up_flag || spk_shut_up || cursor_track == CT_Off) {
William Hubbs3efe8102013-05-13 00:02:56 -05001509 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001510 return;
1511 }
1512 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001513 if (spk_no_intr)
1514 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05001515/* the key press flushes if !no_inter but we want to flush on cursor
Aleksei Fedotov13d825e2015-08-14 22:34:37 +03001516 * moves regardless of no_inter state
1517 */
William Hubbsc6e3fd22010-10-07 13:20:02 -05001518 is_cursor = value + 1;
1519 old_cursor_pos = vc->vc_pos;
1520 old_cursor_x = vc->vc_x;
1521 old_cursor_y = vc->vc_y;
1522 speakup_console[vc->vc_num]->ht.cy = vc->vc_y;
1523 cursor_con = vc->vc_num;
1524 if (cursor_track == CT_Highlight)
1525 reset_highlight_buffers(vc);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001526 cursor_timeout = spk_get_var(CURSOR_TIME);
William Hubbs16d35512010-10-15 22:13:34 -05001527 mod_timer(&cursor_timer,
1528 jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
William Hubbs3efe8102013-05-13 00:02:56 -05001529 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001530}
1531
William Hubbs16d35512010-10-15 22:13:34 -05001532static void update_color_buffer(struct vc_data *vc, const char *ic, int len)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001533{
1534 int i, bi, hi;
1535 int vc_num = vc->vc_num;
1536
Aya Mahfouz6de3f582015-03-04 07:35:28 +02001537 bi = (vc->vc_attr & 0x70) >> 4;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001538 hi = speakup_console[vc_num]->ht.highsize[bi];
1539
1540 i = 0;
1541 if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
1542 speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
1543 speakup_console[vc_num]->ht.rx[bi] = vc->vc_x;
1544 speakup_console[vc_num]->ht.ry[bi] = vc->vc_y;
1545 }
1546 while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
1547 if ((ic[i] > 32) && (ic[i] < 127)) {
1548 speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
1549 hi++;
1550 } else if ((ic[i] == 32) && (hi != 0)) {
William Hubbs16d35512010-10-15 22:13:34 -05001551 if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
1552 32) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001553 speakup_console[vc_num]->ht.highbuf[bi][hi] =
William Hubbs16d35512010-10-15 22:13:34 -05001554 ic[i];
William Hubbsc6e3fd22010-10-07 13:20:02 -05001555 hi++;
1556 }
1557 }
1558 i++;
1559 }
1560 speakup_console[vc_num]->ht.highsize[bi] = hi;
1561}
1562
William Hubbs16d35512010-10-15 22:13:34 -05001563static void reset_highlight_buffers(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001564{
1565 int i;
1566 int vc_num = vc->vc_num;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001567
William Hubbs16d35512010-10-15 22:13:34 -05001568 for (i = 0; i < 8; i++)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001569 speakup_console[vc_num]->ht.highsize[i] = 0;
1570}
1571
William Hubbs16d35512010-10-15 22:13:34 -05001572static int count_highlight_color(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001573{
1574 int i, bg;
1575 int cc;
1576 int vc_num = vc->vc_num;
1577 u16 ch;
1578 u16 *start = (u16 *) vc->vc_origin;
1579
1580 for (i = 0; i < 8; i++)
1581 speakup_console[vc_num]->ht.bgcount[i] = 0;
1582
1583 for (i = 0; i < vc->vc_rows; i++) {
William Hubbs16d35512010-10-15 22:13:34 -05001584 u16 *end = start + vc->vc_cols * 2;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001585 u16 *ptr;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001586
William Hubbsc6e3fd22010-10-07 13:20:02 -05001587 for (ptr = start; ptr < end; ptr++) {
1588 ch = get_attributes(ptr);
1589 bg = (ch & 0x70) >> 4;
1590 speakup_console[vc_num]->ht.bgcount[bg]++;
1591 }
1592 start += vc->vc_size_row;
1593 }
1594
1595 cc = 0;
1596 for (i = 0; i < 8; i++)
1597 if (speakup_console[vc_num]->ht.bgcount[i] > 0)
1598 cc++;
1599 return cc;
1600}
1601
William Hubbs16d35512010-10-15 22:13:34 -05001602static int get_highlight_color(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001603{
1604 int i, j;
Fabian Frederick590aeb12015-06-10 18:33:38 +02001605 unsigned int cptr[8];
William Hubbsc6e3fd22010-10-07 13:20:02 -05001606 int vc_num = vc->vc_num;
1607
1608 for (i = 0; i < 8; i++)
1609 cptr[i] = i;
1610
1611 for (i = 0; i < 7; i++)
1612 for (j = i + 1; j < 8; j++)
1613 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
Fabian Frederick590aeb12015-06-10 18:33:38 +02001614 speakup_console[vc_num]->ht.bgcount[cptr[j]])
1615 swap(cptr[i], cptr[j]);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001616
1617 for (i = 0; i < 8; i++)
1618 if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
1619 if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
1620 return cptr[i];
1621 return -1;
1622}
1623
William Hubbs16d35512010-10-15 22:13:34 -05001624static int speak_highlight(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001625{
1626 int hc, d;
1627 int vc_num = vc->vc_num;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001628
William Hubbsc6e3fd22010-10-07 13:20:02 -05001629 if (count_highlight_color(vc) == 1)
1630 return 0;
1631 hc = get_highlight_color(vc);
1632 if (hc != -1) {
William Hubbs16d35512010-10-15 22:13:34 -05001633 d = vc->vc_y - speakup_console[vc_num]->ht.cy;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001634 if ((d == 1) || (d == -1))
1635 if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y)
1636 return 0;
1637 spk_parked |= 0x01;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001638 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05001639 spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
William Hubbs16d35512010-10-15 22:13:34 -05001640 speakup_console[vc_num]->ht.highsize[hc]);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001641 spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
1642 spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
1643 spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
1644 return 1;
1645 }
1646 return 0;
1647}
1648
William Hubbs16d35512010-10-15 22:13:34 -05001649static void cursor_done(u_long data)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001650{
1651 struct vc_data *vc = vc_cons[cursor_con].d;
1652 unsigned long flags;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001653
William Hubbsc6e3fd22010-10-07 13:20:02 -05001654 del_timer(&cursor_timer);
William Hubbs3efe8102013-05-13 00:02:56 -05001655 spin_lock_irqsave(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001656 if (cursor_con != fg_console) {
1657 is_cursor = 0;
1658 goto out;
1659 }
1660 speakup_date(vc);
1661 if (win_enabled) {
1662 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
William Hubbs16d35512010-10-15 22:13:34 -05001663 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05001664 spk_keydown = is_cursor = 0;
1665 goto out;
1666 }
1667 }
1668 if (cursor_track == read_all_mode) {
1669 handle_cursor_read_all(vc, read_all_key);
1670 goto out;
1671 }
1672 if (cursor_track == CT_Highlight) {
1673 if (speak_highlight(vc)) {
1674 spk_keydown = is_cursor = 0;
1675 goto out;
1676 }
1677 }
1678 if (cursor_track == CT_Window)
1679 speakup_win_say(vc);
1680 else if (is_cursor == 1 || is_cursor == 4)
1681 say_line_from_to(vc, 0, vc->vc_cols, 0);
1682 else
1683 say_char(vc);
1684 spk_keydown = is_cursor = 0;
1685out:
William Hubbs3efe8102013-05-13 00:02:56 -05001686 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001687}
1688
1689/* called by: vt_notifier_call() */
1690static void speakup_bs(struct vc_data *vc)
1691{
1692 unsigned long flags;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001693
William Hubbsc6e3fd22010-10-07 13:20:02 -05001694 if (!speakup_console[vc->vc_num])
1695 return;
William Hubbs3efe8102013-05-13 00:02:56 -05001696 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
William Hubbsc6e3fd22010-10-07 13:20:02 -05001697 /* Speakup output, discard */
1698 return;
1699 if (!spk_parked)
1700 speakup_date(vc);
1701 if (spk_shut_up || synth == NULL) {
William Hubbs3efe8102013-05-13 00:02:56 -05001702 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001703 return;
1704 }
1705 if (vc->vc_num == fg_console && spk_keydown) {
1706 spk_keydown = 0;
1707 if (!is_cursor)
1708 say_char(vc);
1709 }
William Hubbs3efe8102013-05-13 00:02:56 -05001710 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001711}
1712
1713/* called by: vt_notifier_call() */
1714static void speakup_con_write(struct vc_data *vc, const char *str, int len)
1715{
1716 unsigned long flags;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001717
William Hubbsc6e3fd22010-10-07 13:20:02 -05001718 if ((vc->vc_num != fg_console) || spk_shut_up || synth == NULL)
1719 return;
William Hubbs3efe8102013-05-13 00:02:56 -05001720 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
William Hubbsc6e3fd22010-10-07 13:20:02 -05001721 /* Speakup output, discard */
1722 return;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001723 if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1))
William Hubbsc6e3fd22010-10-07 13:20:02 -05001724 bleep(3);
1725 if ((is_cursor) || (cursor_track == read_all_mode)) {
1726 if (cursor_track == CT_Highlight)
1727 update_color_buffer(vc, str, len);
William Hubbs3efe8102013-05-13 00:02:56 -05001728 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001729 return;
1730 }
1731 if (win_enabled) {
1732 if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
William Hubbs16d35512010-10-15 22:13:34 -05001733 vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
William Hubbs3efe8102013-05-13 00:02:56 -05001734 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001735 return;
1736 }
1737 }
1738
1739 spkup_write(str, len);
William Hubbs3efe8102013-05-13 00:02:56 -05001740 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001741}
1742
Sachin Kamat00121962013-05-22 14:37:25 +05301743static void speakup_con_update(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001744{
1745 unsigned long flags;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001746
William Hubbsc6e3fd22010-10-07 13:20:02 -05001747 if (speakup_console[vc->vc_num] == NULL || spk_parked)
1748 return;
William Hubbs3efe8102013-05-13 00:02:56 -05001749 if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
William Hubbsc6e3fd22010-10-07 13:20:02 -05001750 /* Speakup output, discard */
1751 return;
1752 speakup_date(vc);
William Hubbs3efe8102013-05-13 00:02:56 -05001753 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001754}
1755
1756static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
1757{
1758 unsigned long flags;
1759 int on_off = 2;
1760 char *label;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001761
William Hubbsc6e3fd22010-10-07 13:20:02 -05001762 if (synth == NULL || up_flag || spk_killed)
1763 return;
William Hubbs3efe8102013-05-13 00:02:56 -05001764 spin_lock_irqsave(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001765 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001766 if (spk_no_intr)
1767 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05001768 switch (value) {
1769 case KVAL(K_CAPS):
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001770 label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
Alan Cox079c9532012-02-28 14:49:23 +00001771 on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001772 break;
1773 case KVAL(K_NUM):
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001774 label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
Alan Cox079c9532012-02-28 14:49:23 +00001775 on_off = vt_get_leds(fg_console, VC_NUMLOCK);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001776 break;
1777 case KVAL(K_HOLD):
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001778 label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
Alan Cox079c9532012-02-28 14:49:23 +00001779 on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001780 if (speakup_console[vc->vc_num])
1781 speakup_console[vc->vc_num]->tty_stopped = on_off;
1782 break;
1783 default:
1784 spk_parked &= 0xfe;
William Hubbs3efe8102013-05-13 00:02:56 -05001785 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001786 return;
1787 }
1788 if (on_off < 2)
1789 synth_printf("%s %s\n",
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001790 label, spk_msg_get(MSG_STATUS_START + on_off));
William Hubbs3efe8102013-05-13 00:02:56 -05001791 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001792}
1793
William Hubbs16d35512010-10-15 22:13:34 -05001794static int inc_dec_var(u_char value)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001795{
1796 struct st_var_header *p_header;
1797 struct var_t *var_data;
1798 char num_buf[32];
1799 char *cp = num_buf;
1800 char *pn;
1801 int var_id = (int)value - VAR_START;
William Hubbs16d35512010-10-15 22:13:34 -05001802 int how = (var_id & 1) ? E_INC : E_DEC;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001803
William Hubbs16d35512010-10-15 22:13:34 -05001804 var_id = var_id / 2 + FIRST_SET_VAR;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001805 p_header = spk_get_var_header(var_id);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001806 if (p_header == NULL)
1807 return -1;
1808 if (p_header->var_type != VAR_NUM)
1809 return -1;
1810 var_data = p_header->data;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001811 if (spk_set_num_var(1, p_header, how) != 0)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001812 return -1;
1813 if (!spk_close_press) {
1814 for (pn = p_header->name; *pn; pn++) {
1815 if (*pn == '_')
1816 *cp = SPACE;
1817 else
1818 *cp++ = *pn;
1819 }
1820 }
1821 snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
William Hubbs16d35512010-10-15 22:13:34 -05001822 var_data->u.n.value);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001823 synth_printf("%s", num_buf);
1824 return 0;
1825}
1826
William Hubbs16d35512010-10-15 22:13:34 -05001827static void speakup_win_set(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001828{
1829 char info[40];
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001830
William Hubbsc6e3fd22010-10-07 13:20:02 -05001831 if (win_start > 1) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001832 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001833 return;
1834 }
1835 if (spk_x < win_left || spk_y < win_top) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001836 synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001837 return;
1838 }
1839 if (win_start && spk_x == win_left && spk_y == win_top) {
1840 win_left = 0;
William Hubbs16d35512010-10-15 22:13:34 -05001841 win_right = vc->vc_cols - 1;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001842 win_bottom = spk_y;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001843 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
William Hubbs16d35512010-10-15 22:13:34 -05001844 (int)win_top + 1);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001845 } else {
1846 if (!win_start) {
1847 win_top = spk_y;
1848 win_left = spk_x;
1849 } else {
1850 win_bottom = spk_y;
1851 win_right = spk_x;
1852 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001853 snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
Shirish Gajera63b8ebe2015-03-28 13:21:39 -07001854 (win_start) ?
1855 spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
William Hubbs16d35512010-10-15 22:13:34 -05001856 (int)spk_y + 1, (int)spk_x + 1);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001857 }
1858 synth_printf("%s\n", info);
1859 win_start++;
1860}
1861
William Hubbs16d35512010-10-15 22:13:34 -05001862static void speakup_win_clear(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001863{
1864 win_top = win_bottom = 0;
1865 win_left = win_right = 0;
1866 win_start = 0;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001867 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001868}
1869
William Hubbs16d35512010-10-15 22:13:34 -05001870static void speakup_win_enable(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001871{
1872 if (win_start < 2) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001873 synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001874 return;
1875 }
1876 win_enabled ^= 1;
1877 if (win_enabled)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001878 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001879 else
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001880 synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001881}
1882
William Hubbs16d35512010-10-15 22:13:34 -05001883static void speakup_bits(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001884{
1885 int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
Domagoj Trsan8e69a812014-09-09 20:04:34 +02001886
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001887 if (spk_special_handler != NULL || val < 1 || val > 6) {
1888 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001889 return;
1890 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001891 pb_edit = &spk_punc_info[val];
1892 synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
1893 spk_special_handler = edit_bits;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001894}
1895
1896static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
1897{
Christopher Brannon4ea418b2012-06-16 16:55:20 -05001898 static u_char goto_buf[8];
William Hubbs16d35512010-10-15 22:13:34 -05001899 static int num;
Daeseok Younef35a4f2014-04-09 19:45:46 +09001900 int maxlen;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001901 char *cp;
Daeseok Younef35a4f2014-04-09 19:45:46 +09001902
William Hubbsc6e3fd22010-10-07 13:20:02 -05001903 if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
1904 goto do_goto;
1905 if (type == KT_LATIN && ch == '\n')
1906 goto do_goto;
1907 if (type != 0)
1908 goto oops;
1909 if (ch == 8) {
1910 if (num == 0)
1911 return -1;
1912 ch = goto_buf[--num];
1913 goto_buf[num] = '\0';
1914 spkup_write(&ch, 1);
1915 return 1;
1916 }
1917 if (ch < '+' || ch > 'y')
1918 goto oops;
1919 goto_buf[num++] = ch;
1920 goto_buf[num] = '\0';
1921 spkup_write(&ch, 1);
1922 maxlen = (*goto_buf >= '0') ? 3 : 4;
1923 if ((ch == '+' || ch == '-') && num == 1)
1924 return 1;
1925 if (ch >= '0' && ch <= '9' && num < maxlen)
1926 return 1;
William Hubbs16d35512010-10-15 22:13:34 -05001927 if (num < maxlen - 1 || num > maxlen)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001928 goto oops;
1929 if (ch < 'x' || ch > 'y') {
1930oops:
1931 if (!spk_killed)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001932 synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001933 goto_buf[num = 0] = '\0';
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001934 spk_special_handler = NULL;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001935 return 1;
1936 }
Daeseok Younef35a4f2014-04-09 19:45:46 +09001937
1938 goto_pos = simple_strtoul(goto_buf, &cp, 10);
1939
William Hubbsc6e3fd22010-10-07 13:20:02 -05001940 if (*cp == 'x') {
1941 if (*goto_buf < '0')
1942 goto_pos += spk_x;
Daeseok Younef35a4f2014-04-09 19:45:46 +09001943 else if (goto_pos > 0)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001944 goto_pos--;
Daeseok Younef35a4f2014-04-09 19:45:46 +09001945
William Hubbsc6e3fd22010-10-07 13:20:02 -05001946 if (goto_pos >= vc->vc_cols)
William Hubbs16d35512010-10-15 22:13:34 -05001947 goto_pos = vc->vc_cols - 1;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001948 goto_x = 1;
1949 } else {
1950 if (*goto_buf < '0')
1951 goto_pos += spk_y;
Daeseok Younef35a4f2014-04-09 19:45:46 +09001952 else if (goto_pos > 0)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001953 goto_pos--;
Daeseok Younef35a4f2014-04-09 19:45:46 +09001954
William Hubbsc6e3fd22010-10-07 13:20:02 -05001955 if (goto_pos >= vc->vc_rows)
William Hubbs16d35512010-10-15 22:13:34 -05001956 goto_pos = vc->vc_rows - 1;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001957 goto_x = 0;
1958 }
William Hubbs16d35512010-10-15 22:13:34 -05001959 goto_buf[num = 0] = '\0';
William Hubbsc6e3fd22010-10-07 13:20:02 -05001960do_goto:
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001961 spk_special_handler = NULL;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001962 spk_parked |= 0x01;
1963 if (goto_x) {
1964 spk_pos -= spk_x * 2;
1965 spk_x = goto_pos;
1966 spk_pos += goto_pos * 2;
1967 say_word(vc);
1968 } else {
1969 spk_y = goto_pos;
1970 spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
1971 say_line(vc);
1972 }
1973 return 1;
1974}
1975
William Hubbs16d35512010-10-15 22:13:34 -05001976static void speakup_goto(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001977{
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001978 if (spk_special_handler != NULL) {
1979 synth_printf("%s\n", spk_msg_get(MSG_ERROR));
William Hubbsc6e3fd22010-10-07 13:20:02 -05001980 return;
1981 }
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001982 synth_printf("%s\n", spk_msg_get(MSG_GOTO));
1983 spk_special_handler = handle_goto;
William Hubbsc6e3fd22010-10-07 13:20:02 -05001984}
1985
1986static void speakup_help(struct vc_data *vc)
1987{
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01001988 spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
William Hubbsc6e3fd22010-10-07 13:20:02 -05001989}
1990
William Hubbs16d35512010-10-15 22:13:34 -05001991static void do_nothing(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001992{
William Hubbs16d35512010-10-15 22:13:34 -05001993 return; /* flush done in do_spkup */
William Hubbsc6e3fd22010-10-07 13:20:02 -05001994}
William Hubbs16d35512010-10-15 22:13:34 -05001995
William Hubbsc6e3fd22010-10-07 13:20:02 -05001996static u_char key_speakup, spk_key_locked;
1997
William Hubbs16d35512010-10-15 22:13:34 -05001998static void speakup_lock(struct vc_data *vc)
William Hubbsc6e3fd22010-10-07 13:20:02 -05001999{
2000 if (!spk_key_locked)
2001 spk_key_locked = key_speakup = 16;
2002 else
2003 spk_key_locked = key_speakup = 0;
2004}
2005
William Hubbs16d35512010-10-15 22:13:34 -05002006typedef void (*spkup_hand) (struct vc_data *);
Sachin Kamat00121962013-05-22 14:37:25 +05302007static spkup_hand spkup_handler[] = {
William Hubbsc6e3fd22010-10-07 13:20:02 -05002008 /* must be ordered same as defines in speakup.h */
2009 do_nothing, speakup_goto, speech_kill, speakup_shut_up,
2010 speakup_cut, speakup_paste, say_first_char, say_last_char,
2011 say_char, say_prev_char, say_next_char,
2012 say_word, say_prev_word, say_next_word,
2013 say_line, say_prev_line, say_next_line,
2014 top_edge, bottom_edge, left_edge, right_edge,
2015 spell_word, spell_word, say_screen,
2016 say_position, say_attributes,
William Hubbs16d35512010-10-15 22:13:34 -05002017 speakup_off, speakup_parked, say_line, /* this is for indent */
William Hubbsc6e3fd22010-10-07 13:20:02 -05002018 say_from_top, say_to_bottom,
2019 say_from_left, say_to_right,
2020 say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
2021 speakup_bits, speakup_bits, speakup_bits,
2022 speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
2023 speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
2024};
2025
2026static void do_spkup(struct vc_data *vc, u_char value)
2027{
2028 if (spk_killed && value != SPEECH_KILL)
2029 return;
2030 spk_keydown = 0;
2031 spk_lastkey = 0;
2032 spk_shut_up &= 0xfe;
2033 this_speakup_key = value;
2034 if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002035 spk_do_flush();
William Hubbs16d35512010-10-15 22:13:34 -05002036 (*spkup_handler[value]) (vc);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002037 } else {
2038 if (inc_dec_var(value) < 0)
2039 bleep(9);
2040 }
2041}
2042
2043static const char *pad_chars = "0123456789+-*/\015,.?()";
2044
Sachin Kamat00121962013-05-22 14:37:25 +05302045static int
William Hubbsc6e3fd22010-10-07 13:20:02 -05002046speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
William Hubbs16d35512010-10-15 22:13:34 -05002047 int up_flag)
William Hubbsc6e3fd22010-10-07 13:20:02 -05002048{
2049 unsigned long flags;
2050 int kh;
2051 u_char *key_info;
2052 u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
2053 u_char shift_info, offset;
2054 int ret = 0;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02002055
William Hubbsc6e3fd22010-10-07 13:20:02 -05002056 if (synth == NULL)
2057 return 0;
2058
William Hubbs3efe8102013-05-13 00:02:56 -05002059 spin_lock_irqsave(&speakup_info.spinlock, flags);
Greg Kroah-Hartman5b192082010-10-07 19:30:49 -07002060 tty = vc->port.tty;
William Hubbsc6e3fd22010-10-07 13:20:02 -05002061 if (type >= 0xf0)
2062 type -= 0xf0;
William Hubbs16d35512010-10-15 22:13:34 -05002063 if (type == KT_PAD
Alan Cox079c9532012-02-28 14:49:23 +00002064 && (vt_get_leds(fg_console, VC_NUMLOCK))) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05002065 if (up_flag) {
2066 spk_keydown = 0;
2067 goto out;
2068 }
2069 value = spk_lastkey = pad_chars[value];
2070 spk_keydown++;
2071 spk_parked &= 0xfe;
2072 goto no_map;
2073 }
2074 if (keycode >= MAX_KEY)
2075 goto no_map;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002076 key_info = spk_our_keys[keycode];
Sachin Kamatff471ea2013-05-22 16:19:30 +05302077 if (!key_info)
William Hubbsc6e3fd22010-10-07 13:20:02 -05002078 goto no_map;
2079 /* Check valid read all mode keys */
2080 if ((cursor_track == read_all_mode) && (!up_flag)) {
2081 switch (value) {
2082 case KVAL(K_DOWN):
2083 case KVAL(K_UP):
2084 case KVAL(K_LEFT):
2085 case KVAL(K_RIGHT):
2086 case KVAL(K_PGUP):
2087 case KVAL(K_PGDN):
2088 break;
2089 default:
2090 stop_read_all(vc);
2091 break;
2092 }
2093 }
William Hubbs16d35512010-10-15 22:13:34 -05002094 shift_info = (shift_state & 0x0f) + key_speakup;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002095 offset = spk_shift_table[shift_info];
William Hubbsc6e3fd22010-10-07 13:20:02 -05002096 if (offset) {
2097 new_key = key_info[offset];
2098 if (new_key) {
2099 ret = 1;
2100 if (new_key == SPK_KEY) {
2101 if (!spk_key_locked)
2102 key_speakup = (up_flag) ? 0 : 16;
2103 if (up_flag || spk_killed)
2104 goto out;
2105 spk_shut_up &= 0xfe;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002106 spk_do_flush();
William Hubbsc6e3fd22010-10-07 13:20:02 -05002107 goto out;
2108 }
2109 if (up_flag)
2110 goto out;
2111 if (last_keycode == keycode &&
Robin Schroer3d3cb1b2014-06-13 01:17:30 +02002112 time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05002113 spk_close_press = 1;
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002114 offset = spk_shift_table[shift_info + 32];
William Hubbs16d35512010-10-15 22:13:34 -05002115 /* double press? */
William Hubbsc6e3fd22010-10-07 13:20:02 -05002116 if (offset && key_info[offset])
2117 new_key = key_info[offset];
2118 }
2119 last_keycode = keycode;
2120 last_spk_jiffy = jiffies;
2121 type = KT_SPKUP;
2122 value = new_key;
2123 }
2124 }
2125no_map:
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002126 if (type == KT_SPKUP && spk_special_handler == NULL) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05002127 do_spkup(vc, new_key);
2128 spk_close_press = 0;
2129 ret = 1;
2130 goto out;
2131 }
2132 if (up_flag || spk_killed || type == KT_SHIFT)
2133 goto out;
2134 spk_shut_up &= 0xfe;
2135 kh = (value == KVAL(K_DOWN))
William Hubbs16d35512010-10-15 22:13:34 -05002136 || (value == KVAL(K_UP))
2137 || (value == KVAL(K_LEFT))
2138 || (value == KVAL(K_RIGHT));
William Hubbsc6e3fd22010-10-07 13:20:02 -05002139 if ((cursor_track != read_all_mode) || !kh)
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002140 if (!spk_no_intr)
2141 spk_do_flush();
2142 if (spk_special_handler) {
William Hubbsc6e3fd22010-10-07 13:20:02 -05002143 if (type == KT_SPEC && value == 1) {
2144 value = '\n';
2145 type = KT_LATIN;
2146 } else if (type == KT_LETTER)
2147 type = KT_LATIN;
2148 else if (value == 0x7f)
William Hubbs16d35512010-10-15 22:13:34 -05002149 value = 8; /* make del = backspace */
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002150 ret = (*spk_special_handler) (vc, type, value, keycode);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002151 spk_close_press = 0;
2152 if (ret < 0)
2153 bleep(9);
2154 goto out;
2155 }
2156 last_keycode = 0;
2157out:
William Hubbs3efe8102013-05-13 00:02:56 -05002158 spin_unlock_irqrestore(&speakup_info.spinlock, flags);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002159 return ret;
2160}
2161
2162static int keyboard_notifier_call(struct notifier_block *nb,
William Hubbs16d35512010-10-15 22:13:34 -05002163 unsigned long code, void *_param)
William Hubbsc6e3fd22010-10-07 13:20:02 -05002164{
2165 struct keyboard_notifier_param *param = _param;
2166 struct vc_data *vc = param->vc;
2167 int up = !param->down;
2168 int ret = NOTIFY_OK;
William Hubbs16d35512010-10-15 22:13:34 -05002169 static int keycode; /* to hold the current keycode */
William Hubbsc6e3fd22010-10-07 13:20:02 -05002170
2171 if (vc->vc_mode == KD_GRAPHICS)
2172 return ret;
2173
2174 /*
2175 * First, determine whether we are handling a fake keypress on
2176 * the current processor. If we are, then return NOTIFY_OK,
2177 * to pass the keystroke up the chain. This prevents us from
2178 * trying to take the Speakup lock while it is held by the
2179 * processor on which the simulated keystroke was generated.
2180 * Also, the simulated keystrokes should be ignored by Speakup.
2181 */
2182
2183 if (speakup_fake_key_pressed())
2184 return ret;
2185
2186 switch (code) {
2187 case KBD_KEYCODE:
2188 /* speakup requires keycode and keysym currently */
2189 keycode = param->value;
2190 break;
2191 case KBD_UNBOUND_KEYCODE:
2192 /* not used yet */
2193 break;
2194 case KBD_UNICODE:
2195 /* not used yet */
2196 break;
2197 case KBD_KEYSYM:
2198 if (speakup_key(vc, param->shift, keycode, param->value, up))
2199 ret = NOTIFY_STOP;
William Hubbs16d35512010-10-15 22:13:34 -05002200 else if (KTYP(param->value) == KT_CUR)
2201 ret = pre_handle_cursor(vc, KVAL(param->value), up);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002202 break;
William Hubbs16d35512010-10-15 22:13:34 -05002203 case KBD_POST_KEYSYM:{
2204 unsigned char type = KTYP(param->value) - 0xf0;
2205 unsigned char val = KVAL(param->value);
Domagoj Trsan8e69a812014-09-09 20:04:34 +02002206
William Hubbs16d35512010-10-15 22:13:34 -05002207 switch (type) {
2208 case KT_SHIFT:
2209 do_handle_shift(vc, val, up);
2210 break;
2211 case KT_LATIN:
2212 case KT_LETTER:
2213 do_handle_latin(vc, val, up);
2214 break;
2215 case KT_CUR:
2216 do_handle_cursor(vc, val, up);
2217 break;
2218 case KT_SPEC:
2219 do_handle_spec(vc, val, up);
2220 break;
2221 }
William Hubbsc6e3fd22010-10-07 13:20:02 -05002222 break;
William Hubbsc6e3fd22010-10-07 13:20:02 -05002223 }
2224 }
2225 return ret;
2226}
2227
2228static int vt_notifier_call(struct notifier_block *nb,
William Hubbs16d35512010-10-15 22:13:34 -05002229 unsigned long code, void *_param)
William Hubbsc6e3fd22010-10-07 13:20:02 -05002230{
2231 struct vt_notifier_param *param = _param;
2232 struct vc_data *vc = param->vc;
Domagoj Trsan8e69a812014-09-09 20:04:34 +02002233
William Hubbsc6e3fd22010-10-07 13:20:02 -05002234 switch (code) {
2235 case VT_ALLOCATE:
2236 if (vc->vc_mode == KD_TEXT)
2237 speakup_allocate(vc);
2238 break;
2239 case VT_DEALLOCATE:
2240 speakup_deallocate(vc);
2241 break;
2242 case VT_WRITE:
2243 if (param->c == '\b')
2244 speakup_bs(vc);
William Hubbs16d35512010-10-15 22:13:34 -05002245 else if (param->c < 0x100) {
2246 char d = param->c;
Roxana Blaj0a3a7252014-09-14 20:28:53 +03002247
William Hubbs16d35512010-10-15 22:13:34 -05002248 speakup_con_write(vc, &d, 1);
2249 }
William Hubbsc6e3fd22010-10-07 13:20:02 -05002250 break;
2251 case VT_UPDATE:
2252 speakup_con_update(vc);
2253 break;
2254 }
2255 return NOTIFY_OK;
2256}
2257
2258/* called by: module_exit() */
2259static void __exit speakup_exit(void)
2260{
2261 int i;
2262
William Hubbsc6e3fd22010-10-07 13:20:02 -05002263 unregister_keyboard_notifier(&keyboard_notifier_block);
2264 unregister_vt_notifier(&vt_notifier_block);
2265 speakup_unregister_devsynth();
Ben Hutchingsd7500132014-05-19 00:56:22 +01002266 speakup_cancel_paste();
William Hubbsc6e3fd22010-10-07 13:20:02 -05002267 del_timer(&cursor_timer);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002268 kthread_stop(speakup_task);
2269 speakup_task = NULL;
2270 mutex_lock(&spk_mutex);
2271 synth_release();
2272 mutex_unlock(&spk_mutex);
2273
Christopher Brannon628f3422010-12-19 22:50:24 +00002274 speakup_kobj_exit();
2275
2276 for (i = 0; i < MAX_NR_CONSOLES; i++)
2277 kfree(speakup_console[i]);
2278
2279 speakup_remove_virtual_keyboard();
2280
William Hubbsc6e3fd22010-10-07 13:20:02 -05002281 for (i = 0; i < MAXVARS; i++)
2282 speakup_unregister_var(i);
2283
2284 for (i = 0; i < 256; i++) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002285 if (spk_characters[i] != spk_default_chars[i])
2286 kfree(spk_characters[i]);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002287 }
Christopher Brannon628f3422010-12-19 22:50:24 +00002288
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002289 spk_free_user_msgs();
William Hubbsc6e3fd22010-10-07 13:20:02 -05002290}
2291
2292/* call by: module_init() */
2293static int __init speakup_init(void)
2294{
2295 int i;
Christopher Brannon628f3422010-12-19 22:50:24 +00002296 long err = 0;
William Hubbsc6e3fd22010-10-07 13:20:02 -05002297 struct st_spk_t *first_console;
2298 struct vc_data *vc = vc_cons[fg_console].d;
2299 struct var_t *var;
2300
Christopher Brannon628f3422010-12-19 22:50:24 +00002301 /* These first few initializations cannot fail. */
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002302 spk_initialize_msgs(); /* Initialize arrays for i18n. */
2303 spk_reset_default_chars();
2304 spk_reset_default_chartab();
2305 spk_strlwr(synth_name);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002306 spk_vars[0].u.n.high = vc->vc_cols;
William Hubbs16d35512010-10-15 22:13:34 -05002307 for (var = spk_vars; var->var_id != MAXVARS; var++)
William Hubbsc6e3fd22010-10-07 13:20:02 -05002308 speakup_register_var(var);
William Hubbs16d35512010-10-15 22:13:34 -05002309 for (var = synth_time_vars;
2310 (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
William Hubbsc6e3fd22010-10-07 13:20:02 -05002311 speakup_register_var(var);
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002312 for (i = 1; spk_punc_info[i].mask != 0; i++)
Sachin Kamatff471ea2013-05-22 16:19:30 +05302313 spk_set_mask_bits(NULL, i, 2);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002314
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002315 spk_set_key_info(spk_key_defaults, spk_key_buf);
William Hubbsc6e3fd22010-10-07 13:20:02 -05002316
Christopher Brannon628f3422010-12-19 22:50:24 +00002317 /* From here on out, initializations can fail. */
2318 err = speakup_add_virtual_keyboard();
2319 if (err)
2320 goto error_virtkeyboard;
William Hubbsc6e3fd22010-10-07 13:20:02 -05002321
Christopher Brannon628f3422010-12-19 22:50:24 +00002322 first_console = kzalloc(sizeof(*first_console), GFP_KERNEL);
2323 if (!first_console) {
2324 err = -ENOMEM;
2325 goto error_alloc;
2326 }
2327
2328 speakup_console[vc->vc_num] = first_console;
2329 speakup_date(vc);
2330
2331 for (i = 0; i < MAX_NR_CONSOLES; i++)
2332 if (vc_cons[i].d) {
2333 err = speakup_allocate(vc_cons[i].d);
2334 if (err)
2335 goto error_kobjects;
2336 }
2337
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002338 if (spk_quiet_boot)
Christopher Brannon4afaee12011-11-22 13:46:23 -06002339 spk_shut_up |= 0x01;
2340
Christopher Brannon628f3422010-12-19 22:50:24 +00002341 err = speakup_kobj_init();
2342 if (err)
2343 goto error_kobjects;
2344
William Hubbsc6e3fd22010-10-07 13:20:02 -05002345 synth_init(synth_name);
2346 speakup_register_devsynth();
Christopher Brannon628f3422010-12-19 22:50:24 +00002347 /*
2348 * register_devsynth might fail, but this error is not fatal.
2349 * /dev/synth is an extra feature; the rest of Speakup
2350 * will work fine without it.
2351 */
William Hubbsc6e3fd22010-10-07 13:20:02 -05002352
Christopher Brannon628f3422010-12-19 22:50:24 +00002353 err = register_keyboard_notifier(&keyboard_notifier_block);
2354 if (err)
2355 goto error_kbdnotifier;
2356 err = register_vt_notifier(&vt_notifier_block);
2357 if (err)
2358 goto error_vtnotifier;
William Hubbsc6e3fd22010-10-07 13:20:02 -05002359
2360 speakup_task = kthread_create(speakup_thread, NULL, "speakup");
Christopher Brannon628f3422010-12-19 22:50:24 +00002361
William Hubbs7959d552010-12-16 13:26:58 -06002362 if (IS_ERR(speakup_task)) {
Christopher Brannon628f3422010-12-19 22:50:24 +00002363 err = PTR_ERR(speakup_task);
2364 goto error_task;
William Hubbs7959d552010-12-16 13:26:58 -06002365 }
Christopher Brannon628f3422010-12-19 22:50:24 +00002366
2367 set_user_nice(speakup_task, 10);
William Hubbs7959d552010-12-16 13:26:58 -06002368 wake_up_process(speakup_task);
Christopher Brannon628f3422010-12-19 22:50:24 +00002369
2370 pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
2371 pr_info("synth name on entry is: %s\n", synth_name);
William Hubbs7959d552010-12-16 13:26:58 -06002372 goto out;
2373
Christopher Brannon628f3422010-12-19 22:50:24 +00002374error_task:
2375 unregister_vt_notifier(&vt_notifier_block);
2376
2377error_vtnotifier:
2378 unregister_keyboard_notifier(&keyboard_notifier_block);
2379 del_timer(&cursor_timer);
2380
2381error_kbdnotifier:
2382 speakup_unregister_devsynth();
2383 mutex_lock(&spk_mutex);
2384 synth_release();
2385 mutex_unlock(&spk_mutex);
2386 speakup_kobj_exit();
2387
2388error_kobjects:
2389 for (i = 0; i < MAX_NR_CONSOLES; i++)
2390 kfree(speakup_console[i]);
2391
2392error_alloc:
William Hubbs7959d552010-12-16 13:26:58 -06002393 speakup_remove_virtual_keyboard();
Christopher Brannon628f3422010-12-19 22:50:24 +00002394
2395error_virtkeyboard:
2396 for (i = 0; i < MAXVARS; i++)
2397 speakup_unregister_var(i);
2398
2399 for (i = 0; i < 256; i++) {
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002400 if (spk_characters[i] != spk_default_chars[i])
2401 kfree(spk_characters[i]);
Christopher Brannon628f3422010-12-19 22:50:24 +00002402 }
2403
Samuel Thibaultca2beaf2013-01-02 02:37:40 +01002404 spk_free_user_msgs();
Christopher Brannon628f3422010-12-19 22:50:24 +00002405
William Hubbs7959d552010-12-16 13:26:58 -06002406out:
2407 return err;
William Hubbsc6e3fd22010-10-07 13:20:02 -05002408}
2409
William Hubbsc6e3fd22010-10-07 13:20:02 -05002410module_init(speakup_init);
2411module_exit(speakup_exit);