2013-04-16 25 views
7

Tôi đang cố gắng để lấy tọa độ của con trỏ vào một thiết bị đầu cuối vt100 sử dụng đoạn mã sau:Đọc Device Status Report ANSI dãy thoát trả lời

void getCursor(int* x, int* y) { 
    printf("\033[6n"); 
    scanf("\033[%d;%dR", x, y); 
} 

Tôi đang sử dụng chuỗi ANSI thoát sau:

Device Status Report - ESC [6n

báo cáo vị trí con trỏ đến các ứng dụng như (như thể gõ bằng bàn phím) ESC [n; mR, trong đó n là hàng và m là cột.

mã biên dịch và trình tự ANSI được gửi đi, nhưng, khi nhận được nó, nhà ga in các ^[[x;yR chuỗi các stdout thay vì stdin làm cho nó imposible cho tôi để lấy nó ra khỏi chương trình:

terminal window

Rõ ràng, chuỗi được chỉ định cho chương trình, vì vậy, tôi phải làm điều gì đó không chính xác. Có ai biết nó là gì không?

Trả lời

3

Chương trình của bạn đang hoạt động nhưng đang chờ ký tự EOL.

scanf là dòng được định hướng để nó đợi một dòng mới trước khi xử lý. Hãy thử chạy chương trình của bạn và sau đó nhấn phím enter.

Giải pháp là sử dụng một thứ khác không cần dòng mới để đọc đầu vào và sau đó sử dụng sscanf để phân tích các giá trị.

Bạn cũng sẽ cần phải thực hiện stdin không chặn hoặc bạn sẽ không nhận được đầu vào cho đến khi bộ đệm đầy hoặc stdin được đóng lại. Xem câu hỏi này Making stdin non-blocking

Bạn cũng nên gọi fflush(stdout); sau printf để đảm bảo nó được viết thực sự (printf thường được xếp vào bộ đệm nên không có dòng mới có thể không xóa bộ đệm).

+1

Không có cách nào để làm cho lệnh ANSI không ghi ra '^ [[x; yR' cho thiết bị đầu cuối không? Tôi muốn âm thầm lấy các tọa độ mà không có bất kỳ thay đổi có thể nhìn thấy màn hình thiết bị đầu cuối. Nhưng điều này ghi vào thiết bị đầu cuối (không mong muốn khi tạo một GUI văn bản) và do đó thay đổi tọa độ của con trỏ (mà làm cho nó hoàn toàn vô dụng). – Witiko

+0

Đây là lý do tại sao lời nguyền (và ncurses) tồn tại, do đó bạn không phải lo lắng về tất cả các chi tiết này. – Craig

+2

Chắc chắn, nhưng ncurses là bloatware hoàn toàn. Đó là lý do tại sao tôi đang viết một chuỗi ANSI escape sequences của riêng mình, nó chỉ hỗ trợ VT100. Nhưng có, có vẻ như tôi sẽ chỉ cần phải dl ncurses và cố gắng đảo ngược-kỹ sư nó để tìm giải pháp của nó cho vấn đề này. – Witiko

3

Tôi yêu cầu vị trí con trỏ. Nếu tôi không có câu trả lời sau 100ms (điều này là tùy ý), tôi cho rằng giao diện điều khiển không phải là ansi.

/* This function tries to get the position of the cursor on the terminal. 
It can also be used to detect if the terminal is ANSI. 
Return 1 in case of success, 0 otherwise.*/ 

int console_try_to_get_cursor_position(int* x, int *y) 
{ 
    fd_set readset; 
    int success = 0; 
    struct timeval time; 
    struct termios term, initial_term; 

    /*We store the actual properties of the input console and set it as: 
    no buffered (~ICANON): avoid blocking 
    no echoing (~ECHO): do not display the result on the console*/ 
    tcgetattr(STDIN_FILENO, &initial_term); 
    term = initial_term; 
    term.c_lflag &=~ICANON; 
    term.c_lflag &=~ECHO; 
    tcsetattr(STDIN_FILENO, TCSANOW, &term); 

    //We request position 
    print_escape_command("6n"); 
    fflush(stdout); 

    //We wait 100ms for a terminal answer 
    FD_ZERO(&readset); 
    FD_SET(STDIN_FILENO, &readset); 
    time.tv_sec = 0; 
    time.tv_usec = 100000; 

    //If it success we try to read the cursor value 
    if (select(STDIN_FILENO + 1, &readset, NULL, NULL, &time) == 1) 
     if (scanf("\033[%d;%dR", x, y) == 2) success = 1; 

    //We set back the properties of the terminal 
    tcsetattr(STDIN_FILENO, TCSADRAIN, &initial_term); 

    return success; 
} 
0

Tôi tin rằng bạn thực sự nhận được phản hồi mong đợi trong stdin.Nhưng hãy tưởng tượng những gì xảy ra trên thực tế:

  • bạn gửi một yêu cầu như dãy thoát stdout
  • nhà ga nhận nó và formulates một câu trả lời tương ứng như dãy thoát cũng
  • câu trả lời sẽ được gửi đến thiết bị xuất chuẩn
  • scanf được gọi và stdin được chuyển hướng thông qua trình bao mà thư viện readline được sử dụng cho đầu vào tương tác và có thể chỉnh sửa.
  • readline chụp trình tự thoát hơn là chuyển nó đến thiết bị đầu cuối
  • readline tái formulates nó không có nhân vật ESC để ngăn chặn thực hiện các trình tự kiểm soát mà là làm cho nó có thể đọc được bằng cách chỉ sử dụng các ký tự in
  • câu trả lời nhướng đạt scanf nhưng quá muộn
  • câu trả lời nhướng cũng được lặp lại để stdout để người dùng có thể ngay lập tức xem những gì cô ấy đã gõ.

Để tránh điều này, hãy sử dụng vòng lặp getc() (==fgetc(stdin)) thay thế. Nếu bạn gặp ESC (0x1B), hãy bỏ các ký tự sau trong chuỗi cho đến khi bạn tìm được dấu phân cách cuối cùng của chuỗi ESC (trong trường hợp của bạn là 'n'). Sau đó bạn có thể sử dụng sscanf(esqString, formatString, ...).

Nhưng trước khi bạn gặp phải vòng lặp, bạn cần phải change with termios to raw mode (xem ví dụ mã bên dưới). Khác sẽ không có gì khác.

Các vấn đề liên quan