From 4f43140468cefba39573d1efbded5258fcc56c93 Mon Sep 17 00:00:00 2001 From: Michał Cichoń Date: Tue, 25 Aug 2015 06:47:41 +0200 Subject: Port pianobar to Windows: - use newly introduced console.h instead of terminal.h which emulate some behavior of VT terminals - replace ffmpeg/libov player with more abstract one with DirectShow implementation --- src/ui_readline.c | 396 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 256 insertions(+), 140 deletions(-) (limited to 'src/ui_readline.c') diff --git a/src/ui_readline.c b/src/ui_readline.c index eeb5c12..12f4ac8 100644 --- a/src/ui_readline.c +++ b/src/ui_readline.c @@ -21,13 +21,75 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include +//#include +//#include +//#include +//#include +//#include + +#include "ui_readline.h" +#include "console.h" #include -#include -#include #include -#include "ui_readline.h" +static inline char* BarReadlineNextUtf8 (char* ptr) { + if ((*ptr & 0x80) == 0) + return ptr + 1; + else if ((*ptr & 0xE0) == 0xC0) + return ptr + 2; + else if ((*ptr & 0xF0) == 0xE0) + return ptr + 3; + else if ((*ptr & 0xF8) == 0xF0) + return ptr + 4; + else + return ptr; +} + +static inline char* BarReadlinePriorUtf8 (char* ptr) { + while ((*(--ptr) & 0xC0) == 0x80) + /*continue*/; + + return ptr; +} + +static inline int BarReadlineEncodeUtf8 (int codePoint, char* utf8) { + if (codePoint < 0x80) { + utf8[0] = (char)codePoint; + return 1; + } + else if (codePoint < 0x0800) { + utf8[0] = (char)(0xC0 | ((codePoint >> 6) & 0x1F)); + utf8[1] = (char)(0x80 | ((codePoint >> 0) & 0x3F)); + return 2; + } + else if (codePoint < 0x10000) { + utf8[0] = (char)(0xE0 | ((codePoint >> 12) & 0x0F)); + utf8[1] = (char)(0x80 | ((codePoint >> 6) & 0x3F)); + utf8[2] = (char)(0x80 | ((codePoint >> 0) & 0x3F)); + return 3; + } + else if (codePoint < 0x110000) { + utf8[0] = (char)(0xF0 | ((codePoint >> 18) & 0x07)); + utf8[1] = (char)(0x80 | ((codePoint >> 12) & 0x3F)); + utf8[2] = (char)(0x80 | ((codePoint >> 6) & 0x3F)); + utf8[2] = (char)(0x80 | ((codePoint >> 0) & 0x3F)); + return 4; + } + else + return 0; +} + +struct _BarReadline_t { + DWORD DefaultAttr; +}; + +void BarReadlineInit(BarReadline_t* rl) { + static struct _BarReadline_t instance; + *rl = &instance; +} + +void BarReadlineDestroy(BarReadline_t rl) { +} /* return size of previous UTF-8 character */ @@ -46,152 +108,206 @@ static size_t BarReadlinePrevUtf8 (char *ptr) { * @param buffer * @param buffer size * @param accept these characters - * @param input fds + * @param readline * @param flags * @param timeout (seconds) or -1 (no timeout) * @return number of bytes read from stdin */ size_t BarReadline (char *buf, const size_t bufSize, const char *mask, - BarReadlineFds_t *input, const BarReadlineFlags_t flags, int timeout) { - size_t bufLen = 0; - unsigned char escapeState = 0; - fd_set set; - const bool echo = !(flags & BAR_RL_NOECHO); - - assert (buf != NULL); - assert (bufSize > 0); - assert (input != NULL); - - memset (buf, 0, bufSize); - - /* if fd is a fifo fgetc will always return EOF if nobody writes to - * it, stdin will block */ - while (1) { - int curFd = -1; - unsigned char chr; - struct timeval timeoutstruct; - - /* select modifies set and timeout */ - memcpy (&set, &input->set, sizeof (set)); - timeoutstruct.tv_sec = timeout; - timeoutstruct.tv_usec = 0; - - if (select (input->maxfd, &set, NULL, NULL, - (timeout == -1) ? NULL : &timeoutstruct) <= 0) { - /* fail or timeout */ - break; - } + BarReadline_t input, const BarReadlineFlags_t flags, int timeout) { + HANDLE handle = BarConsoleGetStdIn(); + DWORD timeStamp, waitResult; + int bufPos = 0, bufLen = 0; + char* bufOut = buf; - assert (sizeof (input->fds) / sizeof (*input->fds) == 2); - if (FD_ISSET(input->fds[0], &set)) { - curFd = input->fds[0]; - } else if (input->fds[1] != -1 && FD_ISSET(input->fds[1], &set)) { - curFd = input->fds[1]; - } - if (read (curFd, &chr, sizeof (chr)) <= 0) { - /* select() is going wild if fdset contains EOFed stdin, only check - * for stdin, fifo is "reopened" as soon as another writer is - * available - * FIXME: ugly */ - if (curFd == STDIN_FILENO) { - FD_CLR (curFd, &input->set); + const bool overflow = flags & BAR_RL_FULLRETURN; + const bool echo = !(flags & BAR_RL_NOECHO); + + assert(buf != NULL); + assert(bufSize > 0); + assert(input != NULL); + + memset(buf, 0, bufSize); + + if (timeout != INFINITE) { + // convert timeout to ms + timeout *= 1000; + + // get time stamp, required for simulating non-locking input timeouts + timeStamp = GetTickCount(); + } + else + timeStamp = 0; + + while (true) { + if (timeout != INFINITE) { + DWORD now = GetTickCount(); + if ((int)(now - timeStamp) < timeout) { + timeout -= (int)(now - timeStamp); + timeStamp = now; } - continue; + else + timeout = 0; } - switch (chr) { - /* EOT */ - case 4: - /* return */ - case 10: - if (echo) { - fputs ("\n", stdout); - } - buf[bufLen] = '\0'; - return bufLen; - break; - - /* clear line */ - case 21: - if (echo) { - while (bufLen > 0) { - const size_t moveSize = BarReadlinePrevUtf8 (&buf[bufLen]); - assert (bufLen >= moveSize); - - /* move caret and delete character */ - fputs ("\033[D\033[K", stdout); - bufLen -= moveSize; - } - fflush (stdout); - } - bufLen = 0; - break; - - /* escape */ - case 27: - escapeState = 1; - break; - - /* del */ - case 126: - break; - - /* backspace */ - case 8: /* ASCII BS */ - case 127: /* ASCII DEL */ - if (bufLen > 0) { - size_t moveSize = BarReadlinePrevUtf8 (&buf[bufLen]); - assert (bufLen >= moveSize); - memmove (&buf[bufLen-moveSize], &buf[bufLen], moveSize); - - bufLen -= moveSize; - - /* move caret back and delete last character */ - if (echo) { - fputs ("\033[D\033[K", stdout); - fflush (stdout); - } - } - break; - default: - /* ignore control/escape characters */ - if (chr <= 0x1F) { - break; - } - if (escapeState == 2) { - escapeState = 0; - break; - } - if (escapeState == 1 && chr == '[') { - escapeState = 2; - break; - } - /* don't accept chars not in mask */ - if (mask != NULL && !strchr (mask, chr)) { - break; - } - /* don't write beyond buffer's limits */ - if (bufLen < bufSize-1) { - buf[bufLen] = chr; - ++bufLen; - if (echo) { - putchar (chr); - fflush (stdout); - } - /* buffer full => return if requested */ - if (bufLen >= bufSize-1 && (flags & BAR_RL_FULLRETURN)) { + waitResult = WaitForSingleObject(handle, timeout); + + if (WAIT_OBJECT_0 == waitResult) { + INPUT_RECORD inputRecords[8]; + INPUT_RECORD* record; + DWORD recordsRead, i; + + ReadConsoleInput(handle, inputRecords, sizeof(inputRecords) / sizeof(*inputRecords), &recordsRead); + + for (i = 0, record = inputRecords; i < recordsRead; ++i, ++record) + { + int codePoint, keyCode; + + if ((record->EventType != KEY_EVENT) || !record->Event.KeyEvent.bKeyDown) + continue; + + keyCode = record->Event.KeyEvent.wVirtualKeyCode; + codePoint = record->Event.KeyEvent.uChar.UnicodeChar; + + switch (keyCode) { + case VK_LEFT: + if (bufPos > 0) + { + if (echo) { + BarConsoleMoveCursor(-1); + } + bufOut = BarReadlinePriorUtf8(bufOut); + --bufPos; + } + break; + + case VK_RIGHT: + if (bufPos < bufLen) + { + if (echo) { + BarConsoleMoveCursor(1); + } + bufOut = BarReadlineNextUtf8(bufOut); + ++bufPos; + } + break; + + case VK_HOME: if (echo) { - fputs ("\n", stdout); + BarConsoleMoveCursor(-bufPos); + } + bufPos = 0; + bufOut = buf; + break; + + case VK_END: + if (echo) { + BarConsoleMoveCursor(bufLen - bufPos); + } + bufPos = bufLen; + bufOut = buf + strlen(buf); + break; + + case VK_BACK: + if (bufPos > 0) { + int moveSize; + + char* oldBufOut = bufOut; + bufOut = BarReadlinePriorUtf8(bufOut); + moveSize = strlen(bufOut) - (oldBufOut - bufOut); + memmove(bufOut, oldBufOut, moveSize); + bufOut[moveSize] = '\0'; + + if (echo) { + BarConsoleMoveCursor(-1); + BarConsoleEraseCharacter(); + } + + --bufPos; + --bufLen; + } + break; + + case VK_DELETE: + if (bufPos < bufLen) { + int moveSize; + + if (echo) { + BarConsoleEraseCharacter(); + } + + char* nextCharOut = BarReadlineNextUtf8(bufOut); + moveSize = strlen(bufOut) - (bufOut - nextCharOut); + memmove(bufOut, nextCharOut, moveSize); + bufOut[moveSize] = '\0'; + + --bufLen; } - buf[bufLen] = '\0'; + break; + + case VK_RETURN: + if (echo) + fputc('\n', stdout); return bufLen; - } + + default: { + char encodedCodePoint[5]; + int encodedCodePointLength; + + /* + if (keyCode == VK_MEDIA_PLAY_PAUSE) { + codePoint = 'p'; + PlaySoundA("SystemNotification", NULL, SND_ASYNC); + } + else if (keyCode == VK_MEDIA_NEXT_TRACK) { + codePoint = 'n'; + PlaySoundA("SystemNotification", NULL, SND_ASYNC); + } + */ + + if (codePoint <= 0x1F) + break; + + /* don't accept chars not in mask */ + if (mask != NULL && (codePoint > 255 || !strchr(mask, (char)codePoint))) + break; + + encodedCodePointLength = BarReadlineEncodeUtf8(codePoint, encodedCodePoint); + encodedCodePoint[encodedCodePointLength] = '\0'; + + if (bufLen + encodedCodePointLength < (int)bufSize) + { + strncpy(bufOut, encodedCodePoint, encodedCodePointLength); + + if (echo) { + fputs(encodedCodePoint, stdout); + fflush(stdout); + } + + bufOut += encodedCodePointLength; + ++bufPos; + ++bufLen; + + if ((bufLen >= (int)(bufSize - 1)) && overflow) + { + if (echo) + fputc('\n', stdout); + return bufLen; + } + } + } + break; } - break; - } /* end switch */ - } /* end while */ - buf[0] = '\0'; - return 0; + } + } + else if (WAIT_TIMEOUT == waitResult) + break; + else + /* TODO: Handle errors. */ + break; + } + + return bufLen; } /* Read string from stdin @@ -200,7 +316,7 @@ size_t BarReadline (char *buf, const size_t bufSize, const char *mask, * @return number of bytes read from stdin */ size_t BarReadlineStr (char *buf, const size_t bufSize, - BarReadlineFds_t *input, const BarReadlineFlags_t flags) { + BarReadline_t input, const BarReadlineFlags_t flags) { return BarReadline (buf, bufSize, NULL, input, flags, -1); } @@ -208,7 +324,7 @@ size_t BarReadlineStr (char *buf, const size_t bufSize, * @param write result into this variable * @return number of bytes read from stdin */ -size_t BarReadlineInt (int *ret, BarReadlineFds_t *input) { +size_t BarReadlineInt (int *ret, BarReadline_t input) { int rlRet = 0; char buf[16]; @@ -222,7 +338,7 @@ size_t BarReadlineInt (int *ret, BarReadlineFds_t *input) { /* Yes/No? * @param default (user presses enter) */ -bool BarReadlineYesNo (bool def, BarReadlineFds_t *input) { +bool BarReadlineYesNo (bool def, BarReadline_t input) { char buf[2]; BarReadline (buf, sizeof (buf), "yYnN", input, BAR_RL_FULLRETURN, -1); if (*buf == 'y' || *buf == 'Y' || (def == true && *buf == '\0')) { -- cgit v1.2.3