Logo Search packages:      
Sourcecode: fbterm version File versions  Download package

shell.cpp

/*
 *   Copyright  2008 dragchan <zgchan317@gmail.com>
 *   This file is part of FbTerm.
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version 2
 *   of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <time.h>
#include <errno.h>
#include <pty.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <sched.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include "shell.h"

void waitChildProcessExit(s32 pid)
{
      if (pid < 0) return;

      sched_yield();

      s32 ret = waitpid(pid, 0, WNOHANG);
      if (ret > 0 || (ret == -1 && errno == ECHILD)) return;
      
      for (u32 i = 10; i--;) {
            timespec ts = { 0, 100 * 1000000UL };
            nanosleep(&ts, 0);

            ret = waitpid(pid, 0, WNOHANG);
            if (ret > 0) break;
      }
            
      if (ret <= 0) {
            kill(pid, SIGKILL);
            waitpid(pid, 0, 0);
      }
}

Shell::SelectedText Shell::mSelText;

Shell::Shell()
{     
      mPid = -1;
}

Shell::~Shell()
{
      setFd(-1);
      waitChildProcessExit(mPid);
}

void Shell::createChildProcess()
{
      s32 fd;
      mPid = forkpty(&fd, NULL, NULL, NULL);

      switch (mPid) {
      case -1:
            break;
      
      case 0: {  // child process
            initChildProcess();
            setenv("TERM", "linux", 1);

            struct passwd *userpd = getpwuid(getuid());
            execlp(userpd->pw_shell, userpd->pw_shell, NULL);
            exit(1);
            break;
      }

      default:
            setFd(fd);
            setCodec("UTF-8", localCodec());
            resize(w(), h());
            break;
      }
}

void Shell::readyRead(s8 *buf, u32 len)
{
      resetTextSelect();
      input((const u8 *)buf, len);
}

void Shell::sendBack(const s8 *data)
{
      write((s8*)data, strlen(data));
}

void Shell::resize(u16 w, u16 h)
{
      if (!w || !h) return;

      resetTextSelect();
      VTerm::resize(w, h);

      struct winsize size;
      size.ws_xpixel = 0;
      size.ws_ypixel = 0;
      size.ws_col = w;
      size.ws_row = h;
      ioctl(fd(), TIOCSWINSZ, &size);
}

void Shell::keyInput(s8 *buf, u32 len)
{
      resetTextSelect();
      write(buf, len);
}

void Shell::mouseInput(u16 x, u16 y, s32 type, s32 buttons)
{
      if (x >= w()) x = w() - 1;
      if (y >= h()) y = h() - 1;

      s32 btn = buttons & MouseButtonMask;
      s32 modifies = buttons & ModifyButtonMask;
      u16 rtype = mode(MouseReport);
      
      if (btn && rtype != Wheel && (rtype == MouseNone || (modifies & ShiftButton))) {
            textSelect(x, y, type, btn);
            return;
      }
      
      if (rtype == MouseNone) return;

      s32 val = -1;

      switch (type) {
      case Press:
      case DblClick:
            if (btn & LeftButton) val = 0;
            else if (btn & MidButton) val = 1;
            else if (btn & RightButton) val = 2;
            break;
      case Release:
            if (rtype == MouseX11) val = 3;
            break;
      case Wheel:
            if (rtype == MouseX11) {
                  val = 64;
                  if (btn & WheelDown) val |= 1;
            }
            break;
      default:
            break;
      }

      if (rtype == MouseX11 && val != -1) {
            if (modifies & ShiftButton)   val |= 4;
            if (modifies & AltButton) val |= 8;
            if (modifies & ControlButton) val |= 16;
      }

      if (val != -1) {
            s8 buf[8];
            snprintf(buf, sizeof(buf), "\e[M%c%c%c", ' ' + val, ' ' + x + 1, ' ' + y + 1);
            sendBack(buf);
      }
}

void Shell::textSelect(u16 x, u16 y, s32 type, s32 btn)
{
      if (btn == LeftButton) {
            switch (type) {
            case Press:
                  resetTextSelect();
                  mSelState.selecting = true;
                  startTextSelect(x, y);
                  break;
            case Move:
                  if (mSelState.selecting) {
                        middleTextSelect(x, y);
                  } else {
                        resetTextSelect();
                        mSelState.selecting = true;
                        startTextSelect(x, y);
                  }
                  break;
            case Release:
                  mSelState.selecting = false;
                  endTextSelect();
                  break;
            case DblClick:
                  resetTextSelect();
                  autoTextSelect(x, y);
                  break;
            default:
                  break;
            }
      } else if (btn == RightButton) {
            if (type == Press || type == DblClick) {
                  resetTextSelect();
                  putSelectedText();
            }
      }
}

void Shell::startTextSelect(u16 x, u16 y)
{
      mSelState.start = mSelState.end = y * w() + x;

      inverseTextColor(mSelState.start, mSelState.end);
      mSelState.color_inversed = true;
}

void Shell::middleTextSelect(u16 x, u16 y)
{
      u32 start = mSelState.start, end = mSelState.end;
      u32 new_end = y * w() + x;
      
      bool dir_sel = (end >= start);
      bool dir_new_sel = (new_end >= start);
      
      mSelState.end = new_end;
      
      if (dir_sel == dir_new_sel) {
            bool dir_change = (new_end > end);
            
            u32 &pos = (dir_sel == dir_change) ? end : new_end;
            CharAttr attr = charAttr(pos % w(), pos / w());
            
            if (dir_sel) {
                  if (attr.type == CharAttr::DoubleLeft) pos++;
                  pos++;
            } else {
                  if (attr.type == CharAttr::DoubleRight) pos--;
                  pos--;
            }
            
            bool dir_new_change = (new_end == end) ? dir_change : (new_end > end);
            
            if (dir_change == dir_new_change) {
                  inverseTextColor(end, new_end);
            }
      } else {
            inverseTextColor(start, end);
            inverseTextColor(start, new_end);
      }
}

static void utf16_to_utf8(u16 *buf16, u32 num, s8 *buf8)
{
      u16 code;
      u32 index = 0;
      for (; num--; buf16++) {
            code = *buf16;
            if (code >> 11) {
                  buf8[index++] = 0xe0 | (code >> 12);
                  buf8[index++] = 0x80 | ((code >> 6) & 0x3f);
                  buf8[index++] = 0x80 | (code & 0x3f);
            } else if (code >> 7) {
                  buf8[index++] = 0xc0 | ((code >> 6) & 0x1f);
                  buf8[index++] = 0x80 | (code & 0x3f);
            } else {
                  buf8[index++] = code;
            }
      }

      buf8[index] = 0;
}

#define SWAP(a, b) do { \
      if (a > b) { \
            u32 tmp = a; \
            a = b; \
            b = tmp; \
      } \
} while (0)

void Shell::endTextSelect()
{
      u32 start = mSelState.start, end = mSelState.end;
      SWAP(start, end);
      
      u32 len = end - start + 1;
      u16 buf[len];
      s8 *text = new s8[len * 3];

      u16 sx, sy, ex, ey;
      sx = start % w(), sy = start / w();
      ex = end % w(), ey = end / w();

      u32 index = 0;
      for (u16 y = sy; y <= ey; y++) {
            u16 x = (y == sy ? sx : 0);
            u16 end = (y == ey ? ex : (w() -1));
            for (; x <= end; x++) {
                  buf[index++] = charCode(x, y);
                  if (charAttr(x, y).type == CharAttr::DoubleLeft) x++;
            }
      }

      utf16_to_utf8(buf, index, text);
      mSelText.setText(text);
}

void Shell::resetTextSelect()
{
      mSelState.selecting = false;
      if (mSelState.color_inversed) {
            mSelState.color_inversed = false;         
            inverseTextColor(mSelState.start, mSelState.end);
      }
}

void Shell::autoTextSelect(u16 x, u16 y)
{
      static u32 inwordLut[8] = {
            0x00000000,
            0x03FF0000, /* digits */
            0x07FFFFFE, /* uppercase */
            0x07FFFFFE, /* lowercase */
            0x00000000,
            0x00000000,
            0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
            0xFF7FFFFF  /* latin-1 accented letters, not division sign */
      };
      
      static bool inited = false;
      if (!inited) {
            inited = true;
            
            u8 chrs[32];
            initWordChars((s8*)chrs, sizeof(chrs));
            
            for (u32 i = 0; chrs[i]; i++) {
                  if (chrs[i] > 0x7f || chrs[i] <= ' ') continue;
                  inwordLut[chrs[i] >> 5] |= 1 << (chrs[i] & 0x1f);
            }
      }

      #define inword(c) ((c) > 0xff || (( inwordLut[(c) >> 5] >> ((c) & 0x1f) ) & 1))
      #define isspace(c)      ((c) == ' ')

      u16 code = charCode(x, y);
      bool spc = isspace(code);

      u16 sx = x;
      while (1) {
            if (!sx || (spc && !isspace(code)) || (!spc && !inword(code))) break;
            sx--;
            code = charCode(sx, y);
      }

      if (sx < x) sx++;

      code = charCode(x, y);
      spc = isspace(code);

      u16 ex = x;
      while (1) {
            if (ex == w() -1 || (spc && !isspace(code)) || (!spc && !inword(code))) break;
            ex++;
            code = charCode(ex, y);
      }

      if (ex > x) ex--;

      mSelState.start = y * w() + sx;
      mSelState.end = y * w() + ex;
      inverseTextColor(mSelState.start, mSelState.end);
      mSelState.color_inversed = true;
}

void Shell::putSelectedText()
{
      if (mSelText.text) {
            sendBack(mSelText.text);
      }
}

void Shell::inverseTextColor(u32 start, u32 end)
{
      SWAP(start, end);

      u16 sx, sy, ex, ey;
      sx = start % w(), sy = start / w();
      ex = end % w(), ey = end / w();
      
      inverse(sx, sy, ex, ey);

      if (charAttr(sx, sy).type == CharAttr::DoubleRight) sx--;
      if (charAttr(ex, ey).type == CharAttr::DoubleLeft) ex++;

      requestUpdate(sx, sy, (sy == ey ? (ex + 1) : w()) - sx, 1);
      
      if (ey > sy + 1) {
            requestUpdate(0, sy + 1, w(), ey - sy - 1);
      }
      
      if (ey > sy) {
            requestUpdate(0, ey, ex + 1, 1);
      }
}

Generated by  Doxygen 1.6.0   Back to index