]>
Commit | Line | Data |
---|---|---|
c6e3fd22 WH |
1 | #include <linux/slab.h> /* for kmalloc */ |
2 | #include <linux/consolemap.h> | |
3 | #include <linux/interrupt.h> | |
4 | #include <linux/sched.h> | |
5 | #include <linux/selection.h> | |
6 | ||
7 | #include "speakup.h" | |
8 | ||
9 | /* ------ cut and paste ----- */ | |
10 | /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */ | |
11 | #define ishardspace(c) ((c) == ' ') | |
12 | ||
13 | unsigned short xs, ys, xe, ye; /* our region points */ | |
14 | ||
15 | /* Variables for selection control. */ | |
16 | /* must not be disallocated */ | |
17 | struct vc_data *spk_sel_cons; | |
18 | /* cleared by clear_selection */ | |
19 | static int sel_start = -1; | |
20 | static int sel_end; | |
21 | static int sel_buffer_lth; | |
22 | static char *sel_buffer; | |
23 | ||
24 | static unsigned char sel_pos(int n) | |
25 | { | |
a1768fbb WH |
26 | return inverse_translate(spk_sel_cons, |
27 | screen_glyph(spk_sel_cons, n), 0); | |
c6e3fd22 WH |
28 | } |
29 | ||
30 | void speakup_clear_selection(void) | |
31 | { | |
32 | sel_start = -1; | |
33 | } | |
34 | ||
35 | /* does screen address p correspond to character at LH/RH edge of screen? */ | |
36 | static int atedge(const int p, int size_row) | |
37 | { | |
a1768fbb | 38 | return !(p % size_row) || !((p + 2) % size_row); |
c6e3fd22 WH |
39 | } |
40 | ||
41 | /* constrain v such that v <= u */ | |
42 | static unsigned short limit(const unsigned short v, const unsigned short u) | |
43 | { | |
44 | return (v > u) ? u : v; | |
45 | } | |
46 | ||
47 | int speakup_set_selection(struct tty_struct *tty) | |
48 | { | |
49 | int new_sel_start, new_sel_end; | |
50 | char *bp, *obp; | |
51 | int i, ps, pe; | |
52 | struct vc_data *vc = vc_cons[fg_console].d; | |
53 | ||
54 | xs = limit(xs, vc->vc_cols - 1); | |
55 | ys = limit(ys, vc->vc_rows - 1); | |
56 | xe = limit(xe, vc->vc_cols - 1); | |
57 | ye = limit(ye, vc->vc_rows - 1); | |
58 | ps = ys * vc->vc_size_row + (xs << 1); | |
59 | pe = ye * vc->vc_size_row + (xe << 1); | |
60 | ||
61 | if (ps > pe) { | |
62 | /* make sel_start <= sel_end */ | |
63 | int tmp = ps; | |
64 | ps = pe; | |
65 | pe = tmp; | |
66 | } | |
67 | ||
68 | if (spk_sel_cons != vc_cons[fg_console].d) { | |
69 | speakup_clear_selection(); | |
70 | spk_sel_cons = vc_cons[fg_console].d; | |
71 | printk(KERN_WARNING | |
72 | "Selection: mark console not the same as cut\n"); | |
73 | return -EINVAL; | |
74 | } | |
75 | ||
76 | new_sel_start = ps; | |
77 | new_sel_end = pe; | |
78 | ||
79 | /* select to end of line if on trailing space */ | |
80 | if (new_sel_end > new_sel_start && | |
81 | !atedge(new_sel_end, vc->vc_size_row) && | |
82 | ishardspace(sel_pos(new_sel_end))) { | |
83 | for (pe = new_sel_end + 2; ; pe += 2) | |
84 | if (!ishardspace(sel_pos(pe)) || | |
85 | atedge(pe, vc->vc_size_row)) | |
86 | break; | |
87 | if (ishardspace(sel_pos(pe))) | |
88 | new_sel_end = pe; | |
89 | } | |
90 | if ((new_sel_start == sel_start) && (new_sel_end == sel_end)) | |
91 | return 0; /* no action required */ | |
92 | ||
93 | sel_start = new_sel_start; | |
94 | sel_end = new_sel_end; | |
95 | /* Allocate a new buffer before freeing the old one ... */ | |
96 | bp = kmalloc((sel_end-sel_start)/2+1, GFP_ATOMIC); | |
97 | if (!bp) { | |
98 | printk(KERN_WARNING "selection: kmalloc() failed\n"); | |
99 | speakup_clear_selection(); | |
100 | return -ENOMEM; | |
101 | } | |
102 | kfree(sel_buffer); | |
103 | sel_buffer = bp; | |
104 | ||
105 | obp = bp; | |
106 | for (i = sel_start; i <= sel_end; i += 2) { | |
107 | *bp = sel_pos(i); | |
108 | if (!ishardspace(*bp++)) | |
109 | obp = bp; | |
110 | if (!((i + 2) % vc->vc_size_row)) { | |
111 | /* strip trailing blanks from line and add newline, | |
112 | unless non-space at end of line. */ | |
113 | if (obp != bp) { | |
114 | bp = obp; | |
115 | *bp++ = '\r'; | |
116 | } | |
117 | obp = bp; | |
118 | } | |
119 | } | |
120 | sel_buffer_lth = bp - sel_buffer; | |
121 | return 0; | |
122 | } | |
123 | ||
124 | /* TODO: move to some helper thread, probably. That'd fix having to check for | |
125 | * in_atomic(). */ | |
126 | int speakup_paste_selection(struct tty_struct *tty) | |
127 | { | |
128 | struct vc_data *vc = (struct vc_data *) tty->driver_data; | |
129 | int pasted = 0, count; | |
130 | DECLARE_WAITQUEUE(wait, current); | |
131 | add_wait_queue(&vc->paste_wait, &wait); | |
132 | while (sel_buffer && sel_buffer_lth > pasted) { | |
133 | set_current_state(TASK_INTERRUPTIBLE); | |
134 | if (test_bit(TTY_THROTTLED, &tty->flags)) { | |
135 | if (in_atomic()) | |
a1768fbb | 136 | /* if we are in an interrupt handler, abort */ |
c6e3fd22 WH |
137 | break; |
138 | schedule(); | |
139 | continue; | |
140 | } | |
141 | count = sel_buffer_lth - pasted; | |
142 | count = min_t(int, count, tty->receive_room); | |
a1768fbb WH |
143 | tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, |
144 | 0, count); | |
c6e3fd22 WH |
145 | pasted += count; |
146 | } | |
147 | remove_wait_queue(&vc->paste_wait, &wait); | |
148 | current->state = TASK_RUNNING; | |
149 | return 0; | |
150 | } | |
151 |