在C

时间:2019-08-31 09:21:42

标签: c linux posix vt100

我认为我可能在c中有线程问题,但是我不确定。

我的目标是在while(1)循环中执行两个独立的函数,例如: 这些函数之一是kbget(),用于以非规范模式检索在终端中按下的键。 第二个方法是使用ioctl(1,TIOCGWINSZ,...)函数不断获取终端窗口的大小。

通常不起作用,因为while(1)循环在执行第二个函数以重新评估终端窗口大小之前停止从用户那里获得按键。如果在按下某个键之前调整了终端窗口的大小,则除非再次按下一个随机键,否则不会执行评估其大小的功能。

换句话说,调整终端窗口的大小不会更新下面Window结构中的大小值,除非按下一个键。 我希望程序在调整终端大小时更新“实时”的y_size和x_size值。

这是没有POSIX线程的代码中的问题: 用

执行
gcc -Wall scr.h main.c -o main && ./main 

(以下scr.h具有kbget()来更改终端模式):

main.c:

#include "scr.h"
#include <sys/ioctl.h>

#define gotoyx(y, x)       printf("\033[%d;%dH", (y), (x))  // equivalent to move(y, x) in ncurses
#define del_from_cursor(x) printf("\033[%dX", (x))          // delete x characters from cursor position

typedef struct {
    int y_size;
    int x_size;
} Window;

int main(void) 
{
    printf("\033[?1049h\033[2J\033[H"); // remember position & clear screen 

    gotoyx(1, 10);
    printf("Press <ESC> to stop program.");
    gotoyx(2, 10);
    printf("Resizing the terminal window does not 'automatically' update the size shown on screen");

    Window w;
    struct winsize w_s;

    while (1) {

        // evaluate terminal size
        if (!ioctl(1, TIOCGWINSZ, &w_s)) {
            w.y_size = w_s.ws_row;
            w.x_size = w_s.ws_col;
        }

        // print terminal size and center it
        gotoyx(w.y_size / 2, w.x_size / 2);
        del_from_cursor(5);
        printf("w.y_size: %d", w.y_size);
        gotoyx((w.y_size / 2) + 1, w.x_size / 2);
        del_from_cursor(5);
        printf("w.x_size: %d", w.x_size);

        // get key pressed by user in terminal & exit if <ESC> is pressed
        if (kbget() == 0x001b) { break; }       
    }
    printf("\033[2J\033[H\033[?1049l"); // clear screen & restore
    return 0;
}

我尝试使用线程解决此问题,但是到目前为止,我没有成功。

我通过添加2个函数(get_window_size和get_key)修改了上面的main.c文件: (scr.h在get_key()中具有kbget()函数可将终端更改为规范模式)

main.c:

#include "scr.h"
#include <sys/ioctl.h>
#include <pthread.h>

#define gotoyx(y, x)       printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))

typedef struct {
    int y_size;
    int x_size;
} Window;

void *get_window_size(void *arg)
{
    Window *w = (Window *)arg;
    struct winsize w_s;
    if (!ioctl(1, TIOCGWINSZ, &w_s)) {
        w->y_size = w_s.ws_row;
        w->x_size = w_s.ws_col;
    }
    pthread_exit(0);
}

void *get_key(void *arg)
{
    int *key = (int *)arg;
    free(arg);
    *key = kbget();
    int *entered_key = malloc(sizeof(*key));

    *entered_key = *key;
    pthread_exit(entered_key);
}

int main(void)
{
    printf("\033[?1049h\033[2J\033[H");

    Window w;

    pthread_t tid[3];
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_create(&tid[0], &attr, get_window_size, &w);

    int *c = malloc(sizeof(*c));
    int *key_pressed;

    while (1) {
        // for initial size
        pthread_join(tid[0], NULL);

        // printing size to screen
        gotoyx(w.y_size / 2, w.x_size / 2);
        del_from_cursor(5);
        printf("w.y_size: %d", w.y_size);
        gotoyx((w.y_size / 2) + 1, w.x_size / 2);
        del_from_cursor(5);
        printf("w.x_size: %d", w.x_size);

        // get window size
        pthread_attr_t attr1;
        pthread_attr_init(&attr1);
        pthread_create(&tid[1], &attr1, get_window_size, &w);

        // get key entered by user
        pthread_attr_t attr2;
        pthread_attr_init(&attr2);
        pthread_create(&tid[2], &attr2, get_key, c);

        pthread_join(tid[1], NULL);
        pthread_join(tid[2], (void **)&key_pressed);

        if (*key_pressed == 0x001b) {
            break;
        } else {
            free(key_pressed);
        }
    }
    if (key_pressed != NULL) {
        free(key_pressed);
    }
    printf("\033[2J\033[H\033[?1049l");
    return 0;
}

scr.h文件将终端模式更改为非规范(从此处调用以上kbget()函数): 我不认为scr.h中存在任何问题,因为它是从这里(Move the cursor in a C program)提取的。

scr.h:

#ifndef SCR_H
#define SCR_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

struct termios term, oterm;

int getch(void)
{
    int c = 0;
    tcgetattr(STDIN_FILENO, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);
    c = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
    return c;
}

int kbhit(void)
{
    int c = 0;

    tcgetattr(STDIN_FILENO, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 1;
    tcsetattr(STDIN_FILENO, TCSANOW, &term);
    c = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
    if (c != -1) { ungetc(c, stdin); }
    return c != -1 ? 1 : 0;
}

int kbesc(void)
{
    int c = 0;
    if (!kbhit()) { return 0x001b; } // 0x001b is the <ESC> key
    c = getch();
    if (c == 0) { while (kbhit()) { getch(); } }
    return c;
}

int kbget(void)
{
    int c = getch();
    return c == 0x001b ? kbesc() : c; // 0x001b is the <ESC> key
}
#endif // SCR_H

在用valgrind执行时,我在上面的代码中也用pthread出错了Invalid write of size 4

gcc -Wall scr.h main.c -pthread -o main
valgrind -v --leak-check=yes ./main

我知道ncurses和pdcurses的存在。我只是为自己做练习。

更新

我将代码更改为以下代码,不幸的是ret变量从未更改为-1:

#include "scr.h"
#include <errno.h>
#include <sys/ioctl.h>

#define gotoyx(y, x)       printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))

typedef struct {
    int y_size;
    int x_size;
} Window;

static int sigwinch_arrived = 0;

void sigwinch_handler(int signum) 
{ sigwinch_arrived = 1; }

void on_window_size_change(Window *w) 
{
    struct winsize w_s;
    // evaluate terminal size
    if (!ioctl(1, TIOCGWINSZ, &w_s)) {
        w->y_size = w_s.ws_row;
        w->x_size = w_s.ws_col;
    }

    // print terminal size in its center
    gotoyx(w->y_size / 2, w->x_size / 2);
    del_from_cursor(15);
    printf("w.y_size: %d", w->y_size);
    gotoyx((w->y_size / 2) + 1, w->x_size / 2);
    del_from_cursor(15);
    printf("w.x_size: %d", w->x_size);
}

int main(void) 
{
    printf("\033[?1049h\033[2J\033[H"); 

    gotoyx(1, 10);
    printf("Press <ESC> to stop program.");
    gotoyx(2, 10);
    printf("Resizing the terminal window does not 'automatically' update the size shown on screen");

    Window w;

    int ret;
    while (1) {

        // get key pressed by user in terminal & exit if <ESC> is pressed
        ret = kbget();
        gotoyx(10, 10);
        del_from_cursor(8);
        printf("ret: %d", ret);
        if (ret == -1) {
            if (errno == EAGAIN) {
                if (sigwinch_arrived) {
                    sigwinch_arrived = 0;
                    on_window_size_change(&w);
                }
            }
        } else if (ret == 0x001b) { 
            break; 
        }       
    }
    printf("\033[2J\033[H\033[?1049l"); 
    return 0;
}

1 个答案:

答案 0 :(得分:4)

扩展名:根据this answer,如果您的ncurses是使用 SELECT DATE_FORMAT(tbl_date.date, '%b') AS Month, COUNT(tbl_purchase_received_details.purchase_received_id) AS Total_Order, SUM(IFNULL(tbl_purchase_received_details.received_quantity,0)) AS Quantity, IFNULL((tbl_supplier_medicine.price) * SUM(tbl_purchase_received_details.received_quantity),0) AS Total_Amount, tbl_supplier.supplier_name AS Supplier FROM tbl_date LEFT JOIN tbl_purchase_received ON tbl_purchase_received.date_received = tbl_date.date LEFT JOIN tbl_purchase_received_details ON tbl_purchase_received.purchase_received_id = tbl_purchase_received_details.purchase_received_id LEFT JOIN tbl_supplier_medicine ON tbl_supplier_medicine.supplier_medicine_id = tbl_purchase_received_details.supplier_medicine_id LEFT JOIN tbl_supplier ON tbl_supplier.supplier_id = tbl_supplier_medicine.supplier_id WHERE YEAR(tbl_date.date) = YEAR(NOW()) GROUP BY DATE_FORMAT(tbl_date.date, '%b') ORDER BY tbl_date.date 标志编译的,它将自动执行以下解决方案(如果您没有在{{之前覆盖--enable-sigwinch 1}})。在这种情况下,如果发生调整大小事件,SIGWINCHncurses_init())将仅返回getch()


如果控制字符终端的大小更改,则您的进程应收到wgetch()信号(窗口大小更改,在Linux上为信号28)。

它可以由内核(如果在角色桌面上有模式切换)发送,也可以由虚拟终端软件(xterm,gnome-terminal,屏幕等)发送。

如果您的进程收到信号,则其阻塞内核调用(包括KEY_RESIZE)将以错误号SIGWINCH停止。这意味着阻塞呼叫由于到达信号而在时间之前停止。

请注意,在信号处理程序中,您不能做太多事情(例如:否getch()),如果做得最少,则最好做。典型的信号处理程序会更改静态的全局变量,其值由主程序检查。

未经测试的示例代码:

-EAGAIN