Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
selection.c
Go to the documentation of this file.
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 */
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 {
26  return inverse_translate(spk_sel_cons,
27  screen_glyph(spk_sel_cons, n), 0);
28 }
29 
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 {
38  return !(p % size_row) || !((p + 2) % size_row);
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 
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) {
70  spk_sel_cons = vc_cons[fg_console].d;
71  dev_warn(tty->dev,
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  dev_warn(tty->dev, "selection: kmalloc() failed\n");
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(). */
127 {
128  struct vc_data *vc = (struct vc_data *) tty->driver_data;
129  int pasted = 0, count;
132  while (sel_buffer && sel_buffer_lth > pasted) {
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  }
148  current->state = TASK_RUNNING;
149  return 0;
150 }
151