at v3.13 151 lines 3.9 kB view raw
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/device.h> /* for dev_warn */ 6#include <linux/selection.h> 7 8#include "speakup.h" 9 10/* ------ cut and paste ----- */ 11/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */ 12#define ishardspace(c) ((c) == ' ') 13 14unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */ 15 16/* Variables for selection control. */ 17/* must not be deallocated */ 18struct vc_data *spk_sel_cons; 19/* cleared by clear_selection */ 20static int sel_start = -1; 21static int sel_end; 22static int sel_buffer_lth; 23static char *sel_buffer; 24 25static unsigned char sel_pos(int n) 26{ 27 return inverse_translate(spk_sel_cons, 28 screen_glyph(spk_sel_cons, n), 0); 29} 30 31void speakup_clear_selection(void) 32{ 33 sel_start = -1; 34} 35 36/* does screen address p correspond to character at LH/RH edge of screen? */ 37static int atedge(const int p, int size_row) 38{ 39 return !(p % size_row) || !((p + 2) % size_row); 40} 41 42/* constrain v such that v <= u */ 43static unsigned short limit(const unsigned short v, const unsigned short u) 44{ 45 return (v > u) ? u : v; 46} 47 48int speakup_set_selection(struct tty_struct *tty) 49{ 50 int new_sel_start, new_sel_end; 51 char *bp, *obp; 52 int i, ps, pe; 53 struct vc_data *vc = vc_cons[fg_console].d; 54 55 spk_xs = limit(spk_xs, vc->vc_cols - 1); 56 spk_ys = limit(spk_ys, vc->vc_rows - 1); 57 spk_xe = limit(spk_xe, vc->vc_cols - 1); 58 spk_ye = limit(spk_ye, vc->vc_rows - 1); 59 ps = spk_ys * vc->vc_size_row + (spk_xs << 1); 60 pe = spk_ye * vc->vc_size_row + (spk_xe << 1); 61 62 if (ps > pe) { 63 /* make sel_start <= sel_end */ 64 int tmp = ps; 65 ps = pe; 66 pe = tmp; 67 } 68 69 if (spk_sel_cons != vc_cons[fg_console].d) { 70 speakup_clear_selection(); 71 spk_sel_cons = vc_cons[fg_console].d; 72 dev_warn(tty->dev, 73 "Selection: mark console not the same as cut\n"); 74 return -EINVAL; 75 } 76 77 new_sel_start = ps; 78 new_sel_end = pe; 79 80 /* select to end of line if on trailing space */ 81 if (new_sel_end > new_sel_start && 82 !atedge(new_sel_end, vc->vc_size_row) && 83 ishardspace(sel_pos(new_sel_end))) { 84 for (pe = new_sel_end + 2; ; pe += 2) 85 if (!ishardspace(sel_pos(pe)) || 86 atedge(pe, vc->vc_size_row)) 87 break; 88 if (ishardspace(sel_pos(pe))) 89 new_sel_end = pe; 90 } 91 if ((new_sel_start == sel_start) && (new_sel_end == sel_end)) 92 return 0; /* no action required */ 93 94 sel_start = new_sel_start; 95 sel_end = new_sel_end; 96 /* Allocate a new buffer before freeing the old one ... */ 97 bp = kmalloc((sel_end-sel_start)/2+1, GFP_ATOMIC); 98 if (!bp) { 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(). */ 126int 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()) 136 /* if we are in an interrupt handler, abort */ 137 break; 138 schedule(); 139 continue; 140 } 141 count = sel_buffer_lth - pasted; 142 count = min_t(int, count, tty->receive_room); 143 tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, 144 NULL, count); 145 pasted += count; 146 } 147 remove_wait_queue(&vc->paste_wait, &wait); 148 current->state = TASK_RUNNING; 149 return 0; 150} 151