Home | History | Annotate | Download | only in chat
      1 /*
      2  * libjingle
      3  * Copyright 2004--2013, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 // TODO(pmclean): Perhaps this should be unified with examples/call/console.cc
     29 // and refactor to talk/base.
     30 #include "talk/examples/chat/consoletask.h"
     31 
     32 #define _CRT_SECURE_NO_DEPRECATE 1
     33 
     34 #include <stdarg.h>
     35 #ifdef POSIX
     36 #include <signal.h>
     37 #include <termios.h>
     38 #include <unistd.h>
     39 #endif  // POSIX
     40 #include <cassert>
     41 
     42 #include "talk/base/logging.h"
     43 
     44 #ifdef POSIX
     45 static void DoNothing(int unused) {}
     46 #endif
     47 
     48 namespace buzz {
     49 
     50 ConsoleTask::ConsoleTask(talk_base::Thread *thread) :
     51   client_thread_(thread),
     52   console_thread_(new talk_base::Thread()) {
     53 }
     54 
     55 ConsoleTask::~ConsoleTask() {
     56   Stop();
     57 }
     58 
     59 void ConsoleTask::Start() {
     60   if (!console_thread_) {
     61     // stdin was closed in Stop(), so we can't restart.
     62     LOG(LS_ERROR) << "Cannot re-start";
     63     return;
     64   }
     65   if (console_thread_->started()) {
     66     LOG(LS_WARNING) << "Already started";
     67     return;
     68   }
     69   console_thread_->Start();
     70   console_thread_->Post(this, MSG_START);
     71 }
     72 
     73 void ConsoleTask::Stop() {
     74   if (console_thread_ && console_thread_->started()) {
     75 #ifdef WIN32
     76     CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
     77 #else
     78     close(fileno(stdin));
     79     // This forces the read() in fgets() to return with errno = EINTR. fgets()
     80     // will retry the read() and fail, thus returning.
     81     pthread_kill(console_thread_->GetPThread(), SIGUSR1);
     82 #endif
     83     console_thread_->Stop();
     84     console_thread_.reset();
     85   }
     86 }
     87 
     88 void ConsoleTask::SetEcho(bool on) {
     89 #ifdef WIN32
     90   HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
     91   if ((hIn == INVALID_HANDLE_VALUE) || (hIn == NULL))
     92     return;
     93 
     94   DWORD mode;
     95   if (!GetConsoleMode(hIn, &mode))
     96     return;
     97 
     98   if (on) {
     99     mode = mode | ENABLE_ECHO_INPUT;
    100   } else {
    101     mode = mode & ~ENABLE_ECHO_INPUT;
    102   }
    103 
    104   SetConsoleMode(hIn, mode);
    105 #else  // MAC & LINUX
    106   const int fd = fileno(stdin);
    107   if (fd == -1) {
    108     return;
    109   }
    110 
    111   struct termios tcflags;
    112   if (tcgetattr(fd, &tcflags) == -1) {
    113     return;
    114   }
    115 
    116   if (on) {
    117     tcflags.c_lflag |= ECHO;
    118   } else {
    119     tcflags.c_lflag &= ~ECHO;
    120   }
    121 
    122   tcsetattr(fd, TCSANOW, &tcflags);
    123 #endif
    124 }
    125 
    126 void ConsoleTask::Print(const char* format, ...) {
    127   va_list ap;
    128   va_start(ap, format);
    129 
    130   char buf[4096];
    131   int size = vsnprintf(buf, sizeof(buf), format, ap);
    132   assert(size >= 0);
    133   assert(size < static_cast<int>(sizeof(buf)));
    134   buf[size] = '\0';
    135   printf("%s", buf);
    136   fflush(stdout);
    137 
    138   va_end(ap);
    139 }
    140 
    141 void ConsoleTask::RunConsole() {
    142   char input_buffer[128];
    143   while (fgets(input_buffer, sizeof(input_buffer), stdin) != NULL) {
    144     client_thread_->Post(this, MSG_INPUT,
    145         new talk_base::TypedMessageData<std::string>(input_buffer));
    146   }
    147 }
    148 
    149 void ConsoleTask::OnMessage(talk_base::Message *msg) {
    150   switch (msg->message_id) {
    151     case MSG_START:
    152 #ifdef POSIX
    153       // Install a no-op signal so that we can abort RunConsole() by raising
    154       // SIGUSR1.
    155       struct sigaction act;
    156       act.sa_handler = &DoNothing;
    157       sigemptyset(&act.sa_mask);
    158       act.sa_flags = 0;
    159       if (sigaction(SIGUSR1, &act, NULL) < 0) {
    160         LOG(LS_WARNING) << "Can't install signal";
    161       }
    162 #endif
    163       RunConsole();
    164       break;
    165 
    166     case MSG_INPUT:
    167       talk_base::TypedMessageData<std::string> *data =
    168           static_cast<talk_base::TypedMessageData<std::string>*>(msg->pdata);
    169       // Trim off the .line-terminator to make processing easier.
    170       std::string parsed_message =
    171         data->data().substr(0, data->data().length() - 1);
    172       TextInputHandler(parsed_message);
    173       break;
    174   }
    175 }
    176 
    177 }  // namespace buzz
    178