Как сделать неблокирующий ввод с клавиатуры в консольном приложении с помощью Swift? - PullRequest
1 голос
/ 29 апреля 2019

Я хочу сделать простую консольную игру с использованием Swift 5. Мне нужно читать ввод с клавиатуры, не блокируя анимацию игры (используя эмодзи). Игра продолжается, пока нет ввода с клавиатуры, но будет реагировать соответствующим образом, если есть.

Я видел пример того, как это сделать на других языках, таких как C и Python. Я знал, что у Swift есть модуль Дарвина, который обеспечивает много функций POSIX. Однако эти С-коды кажутся несовместимыми со Swift 5.

Например, как преобразовать приведенный ниже код C в Swift? В модуле Дарвина нет ни FD_ZERO, ни FD_SET.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>

struct termios orig_termios;

void reset_terminal_mode()
{
    tcsetattr(0, TCSANOW, &orig_termios);
}

void set_conio_terminal_mode()
{
    struct termios new_termios;

    /* take two copies - one for now, one for later */
    tcgetattr(0, &orig_termios);
    memcpy(&new_termios, &orig_termios, sizeof(new_termios));

    /* register cleanup handler, and set the new terminal mode */
    atexit(reset_terminal_mode);
    cfmakeraw(&new_termios);
    tcsetattr(0, TCSANOW, &new_termios);
}

int kbhit()
{
    struct timeval tv = { 0L, 0L };
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(0, &fds);
    return select(1, &fds, NULL, NULL, &tv);
}

int getch()
{
    int r;
    unsigned char c;
    if ((r = read(0, &c, sizeof(c))) < 0) {
        return r;
    } else {
        return c;
    }
}

int main(int argc, char *argv[])
{
    int key;

    printf("press a key: ");
    fflush(stdout);

    set_conio_terminal_mode();

    while (1) {
        if (kbhit()) {
            key = getch();

            if (key == 13) {
                printf("\n\r");
                break;
            } else if (key >= 20) {
                printf("%c, ", key);
                fflush(stdout);
            }
        }
        else {
            /* do some work */
            printf(".");
            usleep(10);
            printf(".");
            usleep(10);
            printf(".");
            usleep(10);
            printf("\e[3D");
            usleep(10);
        }
    }

    reset_terminal_mode();
}

Я ожидаю, что swift-код сделает то же самое в Swift.

1 Ответ

0 голосов
/ 30 апреля 2019

Функции termios переводят почти один в один в Swift:

#if os(Linux)
import Glibc
#else
import Darwin
#endif

var orig_termios = termios()

func reset_terminal_mode() {
    tcsetattr(0, TCSANOW, &orig_termios)
}

func set_conio_terminal_mode() {
    tcgetattr(0, &orig_termios)
    var new_termios = orig_termios
    atexit(reset_terminal_mode)
    cfmakeraw(&new_termios)
    tcsetattr(0, TCSANOW, &new_termios)
}

set_conio_terminal_mode()

Проблема с select() заключается в том, что FD_ZERO и т. Д. Являются «нетривиальными» макросами и не импортируются в Swift. Но вы можете использовать poll() вместо:

func kbhit() -> Bool {
    var fds = [ pollfd(fd: STDIN_FILENO, events: Int16(POLLIN), revents: 0) ]
    let res = poll(&fds, 1, 0)
    return res > 0
}

Альтернативой является использование Dispatch framework. Вот простой пример, который может помочь вам начать. источник отправки используется для асинхронного ожидания доступного ввода, который затем добавляется в массив, откуда он извлекается в функции getch(). Последовательная очередь используется для синхронизации доступа к массиву.

import Dispatch

let stdinQueue = DispatchQueue(label: "my.serial.queue")
var inputCharacters: [CChar] = []

let stdinSource = DispatchSource.makeReadSource(fileDescriptor: STDIN_FILENO, queue: stdinQueue)
stdinSource.setEventHandler(handler: {
    var c = CChar()
    if read(STDIN_FILENO, &c, 1) == 1 {
        inputCharacters.append(c)
    }
})
stdinSource.resume()

// Return next input character, or `nil` if there is none.
func getch() -> CChar? {
    return stdinQueue.sync {
        inputCharacters.isEmpty ? nil : inputCharacters.remove(at: 0)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...