root/drivers/char/selection.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. highlight
  2. highlight_pointer
  3. clear_selection
  4. inword
  5. sel_loadlut
  6. atedge
  7. limit
  8. set_selection
  9. paste_selection

   1 /*
   2  * linux/drivers/char/selection.c
   3  *
   4  * This module exports the functions:
   5  *
   6  *     'int set_selection(const int arg)'
   7  *     'void clear_selection(void)'
   8  *     'int paste_selection(struct tty_struct *tty)'
   9  *     'int sel_loadlut(const int arg)'
  10  *
  11  * Now that /dev/vcs exists, most of this can disappear again.
  12  */
  13 
  14 #include <linux/tty.h>
  15 #include <linux/sched.h>
  16 #include <linux/mm.h>
  17 #include <linux/malloc.h>
  18 #include "vt_kern.h"
  19 #include "consolemap.h"
  20 #include "selection.h"
  21 
  22 #ifndef MIN
  23 #define MIN(a,b)        ((a) < (b) ? (a) : (b))
  24 #endif
  25 
  26 /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
  27 #define isspace(c)      ((c) == ' ')
  28 
  29 /* Variables for selection control. */
  30 /* Use a dynamic buffer, instead of static (Dec 1994) */
  31        int sel_cons = 0;                /* must not be disallocated */
  32 static volatile int sel_start = -1;     /* cleared by clear_selection */
  33 static int sel_end;
  34 static int sel_buffer_lth = 0;
  35 static char *sel_buffer = NULL;
  36 
  37 #define sel_pos(n)      inverse_translate(screen_word(sel_cons, n, 1) & 0xff)
  38 
  39 /* clear_selection, highlight and highlight_pointer can be called
  40    from interrupt (via scrollback/front) */
  41 
  42 /* set reverse video on characters s-e of console with selection. */
  43 inline static void
  44 highlight(const int s, const int e) {
     /* [previous][next][first][last][top][bottom][index][help] */
  45         invert_screen(sel_cons, s, e-s+2, 1);
  46 }
  47 
  48 /* use complementary color to show the pointer */
  49 inline static void
  50 highlight_pointer(const int where) {
     /* [previous][next][first][last][top][bottom][index][help] */
  51         complement_pos(sel_cons, where);
  52 }
  53 
  54 /* remove the current selection highlight, if any,
  55    from the console holding the selection. */
  56 void
  57 clear_selection(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
  58         highlight_pointer(-1); /* hide the pointer */
  59         if (sel_start != -1) {
  60                 highlight(sel_start, sel_end);
  61                 sel_start = -1;
  62         }
  63 }
  64 
  65 /*
  66  * User settable table: what characters are to be considered alphabetic?
  67  * 256 bits
  68  */
  69 static unsigned long inwordLut[8]={
  70   0x00000000, /* control chars     */
  71   0x03FF0000, /* digits            */
  72   0x87FFFFFE, /* uppercase and '_' */
  73   0x07FFFFFE, /* lowercase         */
  74   0x00000000,
  75   0x00000000,
  76   0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
  77   0xFF7FFFFF  /* latin-1 accented letters, not division sign */
  78 };
  79 
  80 static inline int inword(const unsigned char c) {
     /* [previous][next][first][last][top][bottom][index][help] */
  81         return ( inwordLut[c>>5] >> (c & 0x1F) ) & 1;
  82 }
  83 
  84 /* set inwordLut contents. Invoked by ioctl(). */
  85 int sel_loadlut(const int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  86 {
  87         int i = verify_area(VERIFY_READ, (char *) arg, 36);
  88         if (i)
  89                 return i;
  90         memcpy_fromfs(inwordLut, (unsigned long *)(arg+4), 32);
  91         return 0;
  92 }
  93 
  94 /* does screen address p correspond to character at LH/RH edge of screen? */
  95 static inline int atedge(const int p)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97         return (!(p % video_size_row) || !((p + 2) % video_size_row));
  98 }
  99 
 100 /* constrain v such that v <= u */
 101 static inline unsigned short limit(const unsigned short v, const unsigned short u)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103 /* gcc miscompiles the ?: operator, so don't use it.. */
 104         if (v > u)
 105                 return u;
 106         return v;
 107 }
 108 
 109 /* set the current selection. Invoked by ioctl(). */
 110 int set_selection(const int arg, struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 111 {
 112         int sel_mode, new_sel_start, new_sel_end, spc;
 113         char *bp, *obp;
 114         int i, ps, pe;
 115 
 116         do_unblank_screen();
 117 
 118         { unsigned short *args, xs, ys, xe, ye;
 119 
 120           args = (unsigned short *)(arg + 1);
 121           xs = get_fs_word(args++) - 1;
 122           ys = get_fs_word(args++) - 1;
 123           xe = get_fs_word(args++) - 1;
 124           ye = get_fs_word(args++) - 1;
 125           sel_mode = get_fs_word(args);
 126 
 127           xs = limit(xs, video_num_columns - 1);
 128           ys = limit(ys, video_num_lines - 1);
 129           xe = limit(xe, video_num_columns - 1);
 130           ye = limit(ye, video_num_lines - 1);
 131           ps = ys * video_size_row + (xs << 1);
 132           pe = ye * video_size_row + (xe << 1);
 133 
 134           if (sel_mode == 4) {
 135               /* useful for screendump without selection highlights */
 136               clear_selection();
 137               return 0;
 138           }
 139 
 140           if (mouse_reporting() && (sel_mode & 16)) {
 141               mouse_report(tty, sel_mode & 15, xs, ys);
 142               return 0;
 143           }
 144         }
 145 
 146         if (ps > pe)    /* make sel_start <= sel_end */
 147         {
 148                 int tmp = ps;
 149                 ps = pe;
 150                 pe = tmp;
 151         }
 152 
 153         if (sel_cons != fg_console) {
 154                 clear_selection();
 155                 sel_cons = fg_console;
 156         }
 157 
 158         switch (sel_mode)
 159         {
 160                 case 0: /* character-by-character selection */
 161                         new_sel_start = ps;
 162                         new_sel_end = pe;
 163                         break;
 164                 case 1: /* word-by-word selection */
 165                         spc = isspace(sel_pos(ps));
 166                         for (new_sel_start = ps; ; ps -= 2)
 167                         {
 168                                 if ((spc && !isspace(sel_pos(ps))) ||
 169                                     (!spc && !inword(sel_pos(ps))))
 170                                         break;
 171                                 new_sel_start = ps;
 172                                 if (!(ps % video_size_row))
 173                                         break;
 174                         }
 175                         spc = isspace(sel_pos(pe));
 176                         for (new_sel_end = pe; ; pe += 2)
 177                         {
 178                                 if ((spc && !isspace(sel_pos(pe))) ||
 179                                     (!spc && !inword(sel_pos(pe))))
 180                                         break;
 181                                 new_sel_end = pe;
 182                                 if (!((pe + 2) % video_size_row))
 183                                         break;
 184                         }
 185                         break;
 186                 case 2: /* line-by-line selection */
 187                         new_sel_start = ps - ps % video_size_row;
 188                         new_sel_end = pe + video_size_row
 189                                     - pe % video_size_row - 2;
 190                         break;
 191                 case 3:
 192                         highlight_pointer(pe);
 193                         return 0;
 194                 default:
 195                         return -EINVAL;
 196         }
 197 
 198         /* remove the pointer */
 199         highlight_pointer(-1);
 200 
 201         /* select to end of line if on trailing space */
 202         if (new_sel_end > new_sel_start &&
 203                 !atedge(new_sel_end) && isspace(sel_pos(new_sel_end))) {
 204                 for (pe = new_sel_end + 2; ; pe += 2)
 205                         if (!isspace(sel_pos(pe)) || atedge(pe))
 206                                 break;
 207                 if (isspace(sel_pos(pe)))
 208                         new_sel_end = pe;
 209         }
 210         if (sel_start == -1)    /* no current selection */
 211                 highlight(new_sel_start, new_sel_end);
 212         else if (new_sel_start == sel_start)
 213         {
 214                 if (new_sel_end == sel_end)     /* no action required */
 215                         return 0;
 216                 else if (new_sel_end > sel_end) /* extend to right */
 217                         highlight(sel_end + 2, new_sel_end);
 218                 else                            /* contract from right */
 219                         highlight(new_sel_end + 2, sel_end);
 220         }
 221         else if (new_sel_end == sel_end)
 222         {
 223                 if (new_sel_start < sel_start)  /* extend to left */
 224                         highlight(new_sel_start, sel_start - 2);
 225                 else                            /* contract from left */
 226                         highlight(sel_start, new_sel_start - 2);
 227         }
 228         else    /* some other case; start selection from scratch */
 229         {
 230                 clear_selection();
 231                 highlight(new_sel_start, new_sel_end);
 232         }
 233         sel_start = new_sel_start;
 234         sel_end = new_sel_end;
 235 
 236         if (sel_buffer)
 237                 kfree(sel_buffer);
 238         sel_buffer = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL);
 239         if (!sel_buffer) {
 240                 printk("selection: kmalloc() failed\n");
 241                 clear_selection();
 242                 return -ENOMEM;
 243         }
 244 
 245         obp = bp = sel_buffer;
 246         for (i = sel_start; i <= sel_end; i += 2) {
 247                 *bp = sel_pos(i);
 248                 if (!isspace(*bp++))
 249                         obp = bp;
 250                 if (! ((i + 2) % video_size_row)) {
 251                         /* strip trailing blanks from line and add newline,
 252                            unless non-space at end of line. */
 253                         if (obp != bp) {
 254                                 bp = obp;
 255                                 *bp++ = '\r';
 256                         }
 257                         obp = bp;
 258                 }
 259         }
 260         sel_buffer_lth = bp - sel_buffer;
 261         return 0;
 262 }
 263 
 264 /* Insert the contents of the selection buffer into the queue of the
 265    tty associated with the current console. Invoked by ioctl(). */
 266 int paste_selection(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 267 {
 268         struct wait_queue wait = { current, NULL };
 269         char    *bp = sel_buffer;
 270         int     c = sel_buffer_lth;
 271         int     l;
 272         struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
 273         
 274         if (!bp || !c)
 275                 return 0;
 276         do_unblank_screen();
 277         current->state = TASK_INTERRUPTIBLE;
 278         add_wait_queue(&vt->paste_wait, &wait);
 279         while (c) {
 280                 if (test_bit(TTY_THROTTLED, &tty->flags)) {
 281                         schedule();
 282                         continue;
 283                 }
 284                 l = MIN(c, tty->ldisc.receive_room(tty));
 285                 tty->ldisc.receive_buf(tty, bp, 0, l);
 286                 c -= l;
 287                 bp += l;
 288         }
 289         current->state = TASK_RUNNING;
 290         return 0;
 291 }

/* [previous][next][first][last][top][bottom][index][help] */