ncurses库:一个框架例程源码

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。


        折腾过字符界面下的图形化显示,挺有趣,算是一种偏门需要吧。

目录

1 ncurses库介绍

2 例程介绍和全部源码

3 详解

3.1 主类

3.2 菜单的命令接口

3.3 CWinddow

3.4 CMenu

3.5 MessageLoop


1 ncurses库介绍

        ncurses库是UNIX/LINUX上的字符终端的图形界面库,可以显示菜单,可以用光标控制。使用体验呢,聊胜于无。虽说可以看起来让控制台程序酷一点,但是因为BUG也多,值不值得看项目需要吧。

2 例程介绍和全部源码

        下面就是我写过的一个库,所有代码都在一个文件里。包括菜单、状态栏、提示栏,支持鼠标操作,完整代码如下(建议先编译一下确认代码是可以编译的,然后再看后面的讲解,源码头部有一些特别说明,此源码原来是在CentOS7和8上编译的,64位。代码中如果涉及到“thelog”,这是个日志输出,酌情修改即可):

////yum install ncurses-devel.x86_64
//-lncursesw 带w的支持中文显示,同时必须在使用这个库之前调用setlocale(LC_ALL, "");
// menu库也要带w
//如果键盘码不符合预期(比如F1-F4变成多个字符,也有F5也不对的),设置客户端的终端类型为Xterm#include <locale.h>
#include <curses.h>
#include <menu.h>#define ESC (0x1b)
class CMyCurses
{
public:class ICommandExecute{private:string name;public:ICommandExecute() :name("ICommandExecute") {}ICommandExecute(char const* _name) :name(_name) {}string GetName()const{return name;}virtual bool CommandExecute(){thelog << name << " CommandExecute" << endi;}};class CWinddow{private:WINDOW* m_window = nullptr;//位置和大小int m_y = 0;int m_x = 0;int m_cols = 0;int m_lines = 0;string m_data;//总数据int m_current_begin = 0;//当前显示区域int m_current_end = 0;bool ReCreateWindow(){if (!DestoryWindow())return false;m_window = newwin(m_lines, m_cols, m_y, m_x);keypad(m_window, TRUE);return nullptr != m_window;}bool _ScrollOutput(bool bDown){if (bDown){//结束位置移动到下一行int* pInt = &m_current_end;while (*pInt < m_data.size()){if (m_data[*pInt] != '\n'){++* pInt;}else{++* pInt;break;}}}else{//开始位置移动到前一行int* pInt = &m_current_begin;if (*pInt >= 0 && m_data[*pInt] == '\n'){--* pInt;//跳过前面的换行}while (*pInt >=0){if (m_data[*pInt] != '\n'){--* pInt;}else{break;}}if (*pInt < 0){*pInt = 0;}}while (m_current_end - m_current_begin > 0){wclear(m_window);if (OK == mvwaddnstr(m_window, 0, 0, m_data.c_str() + m_current_begin, m_current_end - m_current_begin)){//thelog << "实际输出 [" << m_current_begin << "," << m_current_end << ")" << endi;return true;}if (bDown){++m_current_begin;}else{--m_current_end;}}return false;}public:bool isCreated()const { return nullptr != m_window; }bool CreateWindow(int lines, int cols, int y, int x){m_y = y;m_x = x;m_cols = cols;m_lines = lines;return ReCreateWindow();}bool DestoryWindow(){if (m_window){if (OK == delwin(m_window)){m_window = nullptr;return true;}else{return false;}}return true;}int GetCh(){Touch();return wgetch(m_window);}void Touch(){touchwin(m_window);}bool RedrawOutput(){wclear(m_window);if (m_current_end - m_current_begin > 0){mvwaddnstr(m_window, 0, 0, m_data.c_str() + m_current_begin, m_current_end - m_current_begin);}wrefresh(m_window);return true;}bool ClearOutput(){m_data = "";m_current_begin = 0;m_current_end = 0;RedrawOutput();}//滚动窗口bool ScrollOutput(bool bDown = true){wclear(m_window);if ((!bDown && 0 == m_current_begin) || (bDown && m_data.size() == m_current_begin)){//已经到顶或到底,当前区域能全部输出则不需要滚动if (ERR == mvwaddnstr(m_window, 0, 0, m_data.c_str() + m_current_begin, m_current_end - m_current_begin)){_ScrollOutput(bDown);}}else{_ScrollOutput(bDown);}wrefresh(m_window);}//追加输出bool DrawOutput(char const* data){m_data += data;m_current_end = m_data.size();if (ERR == waddstr(m_window, data)){thelog << "字符串输出出错" << ende;ScrollOutput();}wrefresh(m_window);return true;}};class CMenu{private:CMyCurses* pMyCurses = nullptr;bool bInited = false;bool bShow = false;//isMainMenu始终显示ICommandExecute* pExec = nullptr;//执行接口bool isMainMenu = false;string name;string description_original;//原始描述string description_show;//输出描述MENU* my_menu = nullptr;WINDOW* my_menu_win = nullptr;WINDOW* my_menu_win_sub = nullptr;ITEM** my_items = nullptr;int my_items_count = 0;vector<CMenu > children;public:CMenu(CMyCurses* p, char const* _name, char const* _description) :pMyCurses(p), name(_name), description_original(_description){}CMenu(CMyCurses* p, char const* _name, char const* _description, ICommandExecute * _pExec) :pMyCurses(p), name(_name), description_original(_description), pExec(_pExec){}bool isInited()const { return bInited; }bool Redraw(int level = 0){string px;px.assign(level * 4, ' ');px += name + ":";if (!isInited()){thelog << px << "尚未初始化" << endi;return true;}if (isMainMenu || bShow){//重新安装菜单,不然由于窗口没有变化,菜单不会刷新显示unpost_menu(my_menu);post_menu(my_menu);set_current_item(my_menu, current_item(my_menu));touchwin(my_menu_win);thelog << px << "刷新my_menu_win" << endi;wrefresh(my_menu_win);thelog << px << "刷新my_menu_win_sub" << endi;wrefresh(my_menu_win_sub);for (auto it = children.begin(); it != children.end(); ++it){thelog << px << "刷新子菜单" << endi;it->Redraw(level + 1);}}else{thelog << px << "不需要显示" << endi;}return true;}CMenu* getChild(int i){return &children[i];}void clearMenuItem(){children.clear();}//返回位置索引(不能是指针,vector扩展时会移动数据)int addMenuItem(CMyCurses* p, char const* name, char const* description){children.push_back(CMenu(p, name, description));return children.size() - 1;}int addMenuItem(CMyCurses* p, char const* name, char const* description, ICommandExecute * pExec){children.push_back(CMenu(p, name, description, pExec));return children.size() - 1;}bool init_main_menu(){isMainMenu = true;return init_menu(1, 0);}bool init_menu(int start_y, int start_x){/* 创建菜单项*/my_items_count = children.size();my_items = (ITEM**)calloc(my_items_count + 1, sizeof(ITEM*));for (int i = 0; i < children.size(); ++i){thelog << "创建子菜单 " << children[i].name << endi;if (children[i].children.size() > 0){if (isMainMenu){children[i].description_show = children[i].description_original + "↓";}else{children[i].description_show = children[i].description_original + "→";}}else{children[i].description_show = children[i].description_original;}my_items[i] = new_item(children[i].name.c_str(), children[i].description_show.c_str());}my_items[children.size()] = nullptr;/* 创建菜单*/my_menu = new_menu((ITEM**)my_items);if (isMainMenu)set_menu_format(my_menu, 1, 16);/* 创建与菜单相关联的窗口*/if (isMainMenu){my_menu_win = newwin(1, COLS, 1, 0);my_menu_win_sub = derwin(my_menu_win, 1, COLS, 0, 0);keypad(my_menu_win, TRUE);}else{int y, x;scale_menu(my_menu, &y, &x);x += 3;//不增加不足以容纳前导的空格和*号,+3后面会多一个空格my_menu_win = newwin(y, x, start_y, start_x);my_menu_win_sub = derwin(my_menu_win, y, x, 0, 0);keypad(my_menu_win, TRUE);}/* 设置主窗口和子窗口*/set_menu_win(my_menu, my_menu_win);set_menu_sub(my_menu, my_menu_win_sub);/* 设置字符串的标记为" * " */set_menu_mark(my_menu, " * ");/* 递送菜单*/post_menu(my_menu);wrefresh(my_menu_win);bInited = true;return true;}//_c主程序传递过来的按键,避免一步额外的操作才能进入菜单,bCmdExecuted 发生了命令执行,需要退出菜单int run_menu(int _c, int level, bool & bCmdExecuted){constexpr int quit_char = ESC;//遇到此键退出int c = _c;do{int post_c = '\0';//如果调用了子菜单,接收子菜单传递回来的字符char buf[1024];sprintf(buf, " %d", c);this->pMyCurses->AddStatus(buf);ITEM* selected = current_item(my_menu);//当前选中项switch (c){case KEY_LEFT:if (!isMainMenu){if (level > 1)return '\0';//二级以上子菜单左键返回上一级,不能传递else return c;}menu_driver(my_menu, REQ_LEFT_ITEM);break;case KEY_RIGHT:if (!isMainMenu){if (!selected)return c;CMenu* pChild = getChild(item_index(selected));if (0 == pChild->children.size())return c;}menu_driver(my_menu, REQ_RIGHT_ITEM);break;case KEY_DOWN:menu_driver(my_menu, REQ_DOWN_ITEM);break;case KEY_UP:menu_driver(my_menu, REQ_UP_ITEM);break;case '\t':if (E_OK != menu_driver(my_menu, REQ_NEXT_ITEM)){menu_driver(my_menu, REQ_FIRST_ITEM);};break;case '\n':{if (selected){CMenu* pChild = getChild(item_index(selected));if (pChild->pExec){thelog << "执行命令" << endi;;pChild->pExec->CommandExecute();bCmdExecuted = true;return '\0';}else{thelog << "命令未配置" << endi;}}break;}break;case KEY_F(1):this->pMyCurses->ShowStatus("显示帮助");break;}thelog << "重绘整个屏幕" << endi;pMyCurses->Redraw();//重绘整个屏幕if (c != '\0'){//重新取得当前选中项,可能已经改变selected = current_item(my_menu);if (selected){sprintf(buf, "用户选中:%s (第%d个)", selected->name.str, item_index(selected) + 1);}else sprintf(buf, "没有有效的选中项");this->pMyCurses->ShowStatus(buf);CMenu* pChild = getChild(item_index(selected));if (0 != pChild->children.size() && (isMainMenu || KEY_RIGHT == c)){int y, x;if (isMainMenu){y = my_menu_win->_begy + 1;x = my_menu_win->_begx + my_menu_win_sub->_parx + selected->x * my_menu->width / my_menu->cols + (0 == selected->x ? 0 : 1);}else{y = my_menu_win->_begy + selected->y;x = my_menu_win->_begx + my_menu_win_sub->_parx + my_menu->width / my_menu->cols;}char buf[1024];sprintf(buf, "用户选中:%s (第%d个 %d %d) _begy %d _begx %d _maxy %d _maxx %d x %d %d (%d %d) %d %d             ", selected->name.str, item_index(selected) + 1, y, x, my_menu_win->_begy, my_menu_win_sub->_begx, my_menu_win->_maxy, my_menu_win_sub->_maxx, my_menu_win->_parx, my_menu_win_sub->_parx, selected->y, selected->x, my_menu->width, my_menu->cols);this->pMyCurses->OutputLine(buf);pChild->bShow = true;thelog << pChild->name << " 初始化子菜单" << endi;pChild->init_menu(y, x);thelog << pChild->name << " 运行子菜单" << endi;post_c = pChild->run_menu('\0', level + 1, bCmdExecuted);thelog << pChild->name << " 清理子菜单" << (bCmdExecuted ? "因为命令已执行" : "无命令执行") << endi;pChild->uninit_menu();pChild->bShow = false;thelog << this->name << " 重绘整个屏幕" << endi;pMyCurses->Redraw();//重绘整个屏幕}}if (post_c != '\0')c = post_c;else c = '\0';if (this->isMainMenu)this->pMyCurses->ShowTip("ESC 退出菜单 Enter 选中 Tab和方向键 改变选择");else this->pMyCurses->ShowTip((name + " ESC 返回上一级 Enter 选中 Tab和方向键 改变选择").c_str());} while (!bCmdExecuted && (c != '\0' || (c = wgetch(my_menu_win)) != quit_char));thelog << "菜单 " << name << " 退出 " << (bCmdExecuted ? "因为命令已执行" : "无命令执行") << endi;return '\0';}bool uninit_menu(){if (bInited){/* 取消递送并释放占用的内存*/unpost_menu(my_menu);if (E_OK != free_menu(my_menu))thelog << "free_menu出错" << ende;for (int i = 0; i < my_items_count; ++i){if (my_items[i])free_item(my_items[i]);}if (ERR == delwin(my_menu_win_sub))thelog << "my_menu_win_sub出错" << ende;if (ERR == delwin(my_menu_win))thelog << "delwin出错" << ende;}my_menu = nullptr;free(my_items);my_menu_win_sub = nullptr;my_menu_win = nullptr;bInited = false;return true;}};
private:string m_title;CMenu m_main_menu;//菜单栏WINDOW* m_TitleLine = nullptr;//标题行CWinddow m_Client;//客户区WINDOW* m_TipLine = nullptr;//提示行WINDOW* m_StateLine = nullptr;//状态行string m_tip;string m_status;bool m_bExit = false;//退出命令//不包含窗口刷新static void print_in_middle(WINDOW* win, int starty, int startx, int width, char const* string, chtype	color){int temp;if (win == NULL)		win = stdscr;temp = (width - strlen(string)) / 2;wattron(win, color);thelog << starty << " " << startx << " " << width << " " << startx + temp << endi;mvwprintw(win, starty, startx + temp, "%s", string);thelog << "输出完成" << endi;wattroff(win, color);}void DrawTip(){wclear(m_TipLine);mvwaddstr(m_TipLine, 0, 0, m_tip.c_str());wrefresh(m_TipLine);}void DrawStatus(){wclear(m_StateLine);mvwaddstr(m_StateLine, 0, 0, m_status.c_str());wrefresh(m_StateLine);}bool MessageLoop(){if (!m_main_menu.isInited())return false;string tip = "ESC 进入菜单 方向键 滚动";ShowTip(tip.c_str());while (!m_bExit){Redraw();ShowTip(tip.c_str());int c = m_Client.GetCh();char buf[1024];sprintf(buf, " %d %x", c, c);ShowStatus(buf);switch (c){case KEY_LEFT:break;case KEY_RIGHT:break;case KEY_DOWN:m_Client.ScrollOutput(true);break;case KEY_UP:m_Client.ScrollOutput(false);break;case '\t':break;case ESC:{bool bCmdExecuted = false;m_main_menu.run_menu(c, 0, bCmdExecuted);}break;case KEY_MOUSE:ShowStatus("KEY_MOUSE");MEVENT event;char buf2[256];char buf[256];if (getmouse(&event) == OK){if (event.bstate & BUTTON1_RELEASED)sprintf(buf2, "BUTTON1_RELEASED	     ");else if (event.bstate & BUTTON1_PRESSED)sprintf(buf2, "BUTTON1_PRESSED		   ");else if (event.bstate & BUTTON1_CLICKED)sprintf(buf2, "BUTTON1_CLICKED	     ");else if (event.bstate & BUTTON1_DOUBLE_CLICKED)sprintf(buf2, "BUTTON1_DOUBLE_CLICKED");else if (event.bstate & BUTTON1_TRIPLE_CLICKED)sprintf(buf2, "BUTTON1_TRIPLE_CLICKED");else if (event.bstate & BUTTON2_RELEASED)sprintf(buf2, "BUTTON2_RELEASED	     ");else if (event.bstate & BUTTON2_PRESSED)sprintf(buf2, "BUTTON2_PRESSED		   ");else if (event.bstate & BUTTON2_CLICKED)sprintf(buf2, "BUTTON2_CLICKED		   ");else if (event.bstate & BUTTON2_DOUBLE_CLICKED)sprintf(buf2, "BUTTON2_DOUBLE_CLICKED");else if (event.bstate & BUTTON2_TRIPLE_CLICKED)sprintf(buf2, "BUTTON2_TRIPLE_CLICKED");else if (event.bstate & BUTTON3_RELEASED)sprintf(buf2, "BUTTON3_RELEASED	     ");else if (event.bstate & BUTTON3_PRESSED)sprintf(buf2, "BUTTON3_PRESSED		   ");else if (event.bstate & BUTTON3_CLICKED)sprintf(buf2, "BUTTON3_CLICKED		   ");else if (event.bstate & BUTTON3_DOUBLE_CLICKED)sprintf(buf2, "BUTTON3_DOUBLE_CLICKED");else if (event.bstate & BUTTON3_TRIPLE_CLICKED)sprintf(buf2, "BUTTON3_TRIPLE_CLICKED");else if (event.bstate & BUTTON4_RELEASED)sprintf(buf2, "BUTTON4_RELEASED	     ");else if (event.bstate & BUTTON4_PRESSED){m_Client.ScrollOutput(false);sprintf(buf2, "BUTTON4_PRESSED wheel up");//滚轮向上}else if (event.bstate & BUTTON4_CLICKED)sprintf(buf2, "BUTTON4_CLICKED		   ");else if (event.bstate & BUTTON4_DOUBLE_CLICKED)sprintf(buf2, "BUTTON4_DOUBLE_CLICKED");else if (event.bstate & BUTTON4_TRIPLE_CLICKED)sprintf(buf2, "BUTTON4_TRIPLE_CLICKED");else{sprintf(buf2, "other");}}else{m_Client.ScrollOutput(true);sprintf(buf2, "wheel down");//滚轮向下,bstate为0}sprintf(buf, "y=%d x=%d z=%d state=%X %s", event.y, event.x, event.z, event.bstate, buf2);this->ShowStatus(buf);break;case KEY_F(1):ShowStatus("帮助");break;}}return true;}class CCommand_Refresh : public ICommandExecute{private:CMyCurses* pCurses;public:CCommand_Refresh(CMyCurses* _pCurses, char const* name) :CMyCurses::ICommandExecute(name), pCurses(_pCurses){}virtual bool CommandExecute() override{pCurses->Redraw();return true;}};class CCommand_Exit : public ICommandExecute{private:CMyCurses* pCurses;public:CCommand_Exit(CMyCurses* _pCurses, char const* name) :CMyCurses::ICommandExecute(name), pCurses(_pCurses){}virtual bool CommandExecute() override{pCurses->SetExit();return true;}};
public:CMyCurses() :m_main_menu(this, "main_menu", "") {int i = m_main_menu.addMenuItem(this, "系统", "");m_main_menu.getChild(i)->addMenuItem(this, "刷新", "", new CCommand_Refresh(this, "__REFRESH"));m_main_menu.getChild(i)->addMenuItem(this, "退出", "", new CCommand_Exit(this, "__EXIT"));}CMenu* GetMenu() { return &m_main_menu; }void SetExit() { m_bExit = true; }void ShowTip(char const* tip){m_tip = tip;DrawTip();}void ShowStatus(char const* status){m_status = status;DrawStatus();}void AddStatus(char const* status){m_status += status;DrawStatus();}void CreateTestMenu(){int i;i = m_main_menu.addMenuItem(this, "配置", "");m_main_menu.getChild(i)->addMenuItem(this, "配置1", "显示配置", new ICommandExecute("配置1"));m_main_menu.getChild(i)->addMenuItem(this, "配置2", "显示配置");m_main_menu.getChild(i)->addMenuItem(this, "配置3", "显示配置");i = m_main_menu.addMenuItem(this, "任务", "");m_main_menu.getChild(i)->addMenuItem(this, "任务1", "显示配置");m_main_menu.getChild(i)->addMenuItem(this, "任务2", "显示配置");m_main_menu.getChild(i)->addMenuItem(this, "任务3", "显示配置");m_main_menu.getChild(i)->getChild(1)->addMenuItem(this, "任务x", "显示配置", new ICommandExecute("任务x"));i = m_main_menu.addMenuItem(this, "状态", "");m_main_menu.getChild(i)->addMenuItem(this, "状态1", "显示状态");i = m_main_menu.addMenuItem(this, "帮助", "");}bool CursesMain(char const* title){thelog.setOutput(false);m_title = title;m_bExit = false;setlocale(LC_ALL, "");initscr();start_color();cbreak();noecho();keypad(stdscr, TRUE);init_pair(1, COLOR_RED, COLOR_BLACK);clear();move(0, 0);refresh();//初始化鼠标系统mousemask(ALL_MOUSE_EVENTS, NULL);//屏幕区域m_TitleLine = newwin(1, COLS, 0, 0);//标题栏,第一行//m_main_menu 菜单栏,第二行m_Client.CreateWindow(LINES - 4, COLS, 2, 0);//客户区,中间部分m_TipLine = newwin(1, COLS, LINES - 2, 0);//提示行,倒数第二行m_StateLine = newwin(1, COLS, LINES - 1, 0);//状态栏,倒数第一行ClearOutput();OutputLine("1234");m_main_menu.init_main_menu();Redraw();MessageLoop();return true;}bool Redraw(){if (!m_TitleLine)thelog << "m_TitleLine is nullptr" << ende;if (!m_Client.isCreated())thelog << "m_Client is nullptr" << ende;if (!m_TipLine)thelog << "m_TipLine is nullptr" << ende;if (!m_StateLine)thelog << "m_StateLine is nullptr" << ende;thelog << "开始重绘整个屏幕" << endi;clear();refresh();//没有这句后面的就显示不出来//标题thelog << "重绘标题" << endi;wclear(m_TitleLine);print_in_middle(m_TitleLine, 0, 0, COLS, m_title.c_str(), COLOR_PAIR(1));wrefresh(m_TitleLine);m_Client.RedrawOutput();DrawTip();DrawStatus();//菜单thelog << "重绘主菜单" << endi;m_main_menu.Redraw();return true;}bool uninit(){m_main_menu.uninit_menu();m_Client.DestoryWindow();delwin(m_TitleLine);delwin(m_TipLine);delwin(m_StateLine);thelog.setOutput(true);if (ERR == endwin())thelog << "endwin error" << ende;;return true;}bool Output(char const* data){return m_Client.DrawOutput(data);}bool OutputLine(char const* data){return m_Client.DrawOutput(data) && m_Client.DrawOutput("\n");}bool ClearOutput(){m_Client.ClearOutput();return m_Client.RedrawOutput();}static int CMyCurses_test(){initscr();clear();move(10, 20);addstr("hello,world");move(LINES - 1, 0);refresh();getch();WINDOW* w1 = newwin(10, 10, 0, 0);WINDOW* w2 = newwin(10, 10, 5, 5);box(w1, 0, 0);box(w2, 0, 0);move(0, 0);waddstr(w1, "111111111111111111111111111111111111111111111111111111111111111111111111111111111111");wrefresh(w1);move(0, 0);waddstr(w2, "2222222222222222222");move(1, 0);waddstr(w2, "3333");wrefresh(w2);getch();mvaddstr(LINES - 1, 0, "3");refresh();//box(w1, 0, 0);wmove(w1, 0, 0);waddch(w1, 'a');waddch(w1, 'a');waddch(w1, 'a');waddch(w1, 'a');waddch(w1, 'a');wrefresh(w1);getch();endwin();return 0;}
};

3 详解

3.1 主类

        主类CMyCurses,一切都在这个类里面。

3.2 菜单的命令接口

        ICommandExecute,添加菜单时可以用一个命令作为参数(父菜单一般没有命令),选择这个菜单并回车导致执行命令。

3.3 CWinddow

        CWinodw是窗口,是屏幕上的一块矩形区域。数据成员主要包括起始位置、行列数,要显示的数据,当前显示位置。要显示的数据不一定能全部显示,所以要记录当前显示到哪里了。

        窗口的主要方法是滚动显示。

3.4 CMenu

        CMenu是菜单,是一个树形结构。菜单是整个界面的控制核心。

        菜单的核心是run_menu,根据键盘控制改变选中项并执行命令。

3.5 MessageLoop

        主窗口的消息循环,最重要的功能是按下ESC键进入到菜单的消息循环。

        判断按键和鼠标,执行相应的操作。如果是鼠标操作(KEY_MOUSE)需要额外调用getmouse(&event)来获取鼠标状态。

3.6 主入口CursesMain

        这是类的主入口。里面会初始化系统并进入MessageLoop循环。

        初始化相当繁琐:

        setlocale(LC_ALL, "");initscr();start_color();cbreak();noecho();keypad(stdscr, TRUE);init_pair(1, COLOR_RED, COLOR_BLACK);clear();move(0, 0);refresh();//初始化鼠标系统mousemask(ALL_MOUSE_EVENTS, NULL);

        然后才是设置自己的数据,最后执行消息循环。 

3.7 纯测试代码CMyCurses_test

        这个对ncurses库的测试,如果有什么函数不太明白,在这里改一改看效果。

(这里是结束)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/790582.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3D雷达和相机联合标定:一种灵活且精确的基于目标的外参标定方法

3D雷达和相机联合标定&#xff1a;一种灵活且精确的基于目标的外参标定方法 论文链接&#xff1a;https://arxiv.org/pdf/2307.15264.pdf 附赠自动驾驶学习资料和量产经验&#xff1a;链接 摘要 本文介绍了3D雷达和相机联合标定&#xff1a;一种灵活且精确的基于目标的外参…

精通并发【基础三】:线程安全有哪些思路

不可变 final无锁编程&#xff0c;将数据处理映射到指定的线程中处理ThreadLocal互斥&#xff08;SyncReentrantLockCAS&#xff09; 在多线程编程中&#xff0c;线程安全是指当多个线程访问某个类时&#xff0c;这个类始终能表现出正确的行为。实现线程安全可以通过多种方式&…

Android 10.0 双sim卡区分SIM卡1和SIM卡2来电通知功能实现

1.前言 在10.0的rom系统定制化开发中,在一些产品支持双卡双待的功能中,如果两张sim卡都是移动卡,在来电通知中,就不容易区分 到底是哪张sim卡来的电话信息都显示移动信息 所以为了区分sim卡就需要在sim描述的时候 添加是哪张sim卡,接下来就来实现 这些功能 2.双sim卡区…

【C++】编程规范之表达式原则

表达式中变量的位置 在编写表达式时&#xff0c;将变量放置在右边&#xff0c;可以提高代码的可读性和可理解性。这种做法符合自然语言的阅读习惯&#xff0c;使得代码更易于理解。 // Good if (5 x) {// do something }// Avoid if (x 5) {// do something }不变量和资源申…

WPF中动画教程(DoubleAnimation的基本使用)

实现效果 今天以一个交互式小球的例子跟大家分享一下wpf动画中DoubleAnimation的基本使用。该小球会移动到我们鼠标左键或右键点击的地方。 该示例的实现效果如下所示&#xff1a; 页面设计 xaml如下所示&#xff1a; <Window x:Class"AnimationDemo.MainWindow&qu…

vue使用iview导航栏Menu activeName不生效

activeName不生效 一、问题一、解决方案&#xff0c; 一、问题 根据ivew官网的提示&#xff0c;设置了active-name和open-names以后&#xff0c;发现不管是设置静态是数据还是设置动态的数据&#xff0c;都不生效 一、解决方案&#xff0c; 在设置动态名称的时候&#xff0c…

【Erlang】Linux(CentOS7)安装Erlang和RabbitMQ

一、系统环境 查版本对应&#xff0c;CentOS-7&#xff0c;选择Erlang 23.3.4&#xff0c;RabbitMQ 3.9.16 二、操作步骤 安装 Erlang repository curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash安装 Erlang package s…

Yocto理论基础之layer

Yocto理论基础之layer 一、layer介绍二、layer最佳实践三、创建layer 一、layer介绍 在poky源码中我们基本上在每一个meta layer中都能看到一个layer.conf,里面大致内容如下&#xff1a; # We have a conf and classes directory, add to BBPATH BBPATH . ":${LAYERDIR}…

如何使用Python进行文件读写操作?

如何使用Python进行文件读写操作&#xff1f; Python是一种功能强大的编程语言&#xff0c;它提供了丰富的库和工具&#xff0c;使得文件读写操作变得简单而高效。在Python中&#xff0c;可以使用内置的open()函数来进行文件读写操作。下面将详细介绍如何使用Python进行文件读…

【SpringCloud】Ribbon 负载均衡

目 录 一.负载均衡原理二.源码跟踪1. LoadBalancerIntercepor2. LoadBalancerClient3. 负载均衡策略 IRule4. 总结 三.负载均衡策略1.负载均衡策略2.自定义负载均衡策略 四.饥饿加载 在 order-service 中 添加了 LoadBalanced 注解&#xff0c;即可实现负载均衡功能&#xff0c…

Mysql数据库的存储引擎的区别

MyISAM引擎 MyISAM是MySQL的默认数据库引擎&#xff08;5.5版之前&#xff09;。虽然性能极佳&#xff0c;而且提供了大量的特性&#xff0c;包括全文索引、压缩、空间函数等&#xff0c;但MyISAM不支持事务和行级锁&#xff0c;而且最大的缺陷就是崩溃后无法安全恢复。 Inno…

5.2 SSH和交换机端口安全概述

交换机的安全是一个很重要的问题&#xff0c;因为它可能会遭受到一些恶意的攻击&#xff0c;例如MAC泛洪攻击、DHCP欺骗和耗竭攻击、中间人攻击、CDP 攻击和Telnet DoS 攻击等&#xff0c;为了防止交换机被攻击者探测或者控制&#xff0c;必须采取相应的措施来确保交换机的安全…

Spark面试整理-讨论DataFrame和DataSet的区别

在Apache Spark中,DataFrame和Dataset是两种核心的数据结构,它们用于处理结构化数据。尽管它们有很多相似之处,但也存在一些关键的区别。理解这些区别有助于在不同的应用场景中做出适当的选择。 DataFrame 定义:DataFrame是一个分布式的数据集合,类似于关系数据库中的表格…

【Error】Uncaught TypeError: Cannot read properties of undefined (reading ‘get’)

报错原因&#xff1a; 返回值为undefined 解决&#xff1a; vue3可用&#xff1f;

高项(2)信息化和信息系统基础知识1-软件测试-软件需求-软件架构-中间件-数据仓库-七层协议

1.国家信息化体系6要素 法律法规&#xff0c;是保障信息资源&#xff0c;信息资源的开发和利用是核心任务&#xff0c;是国家信息化取得实效的关键信息网络&#xff0c;是基础信息技术应用&#xff0c;是6要素中的龙头信息技术和产业&#xff0c;是物质保障信息化人才&#xff…

RUST语言变量与数据类型使用

使用之前了解: fn main() 表示程序入口点 println!("要输出的内容"); 表示格式化输出 变量与常量声明: let 变量:变量类型 变量值;let mut 变量:变量类型 变量值; const 常量:常量类型 常量值 如果 声明时不指定类型,将根据赋值类型自动推导 变量类型参与下…

在@Value中的Spring EL的使用

在 Spring 中&#xff0c;${} 语法用于引用配置文件中的属性值&#xff0c;而 #{} 语法用于使用 SpEL&#xff08;Spring Expression Language&#xff09;表达式。 Spring EL&#xff08;Expression Language&#xff09;是一种强大的表达式语言&#xff0c;用于在 Spring 应用…

wife_wife【web 攻防世界】

大佬的wp:WEB&#xff1a;Wife_wife-CSDN博客 知识点&#xff1a; prototype是new class 的一个属性&#xff0c;即__proto__指向new class 的prototype属性__proto__如果作为json代码解析的话会被当成键名处理&#xff0c;但是如果是在类中的话则会被当成子类的原型 如let o…

如何防止IP泄露,安全匿名上网?

当互联网成为每个家庭的重要组成部分后&#xff0c;IP地址就成了你的虚拟地址。您的请求从该地址开始&#xff0c;然后 Internet 将消息发送回该地址。那么&#xff0c;您担心您的地址被泄露吗&#xff1f; 对于安全意识高或者某些业务需求的用户&#xff0c;如果您正在寻找保护…

【Spring】使用@Bean和@Import注解配置Bean,与Bean的实例化

目录 1、bean是什么 2、配置bean 2.1、使用Bean注解配置Bean 2.2、使用Import注解配置Bean 3、实例化Bean 1、bean是什么 在 Spring 中&#xff0c;Bean 是指由 Spring 容器管理的对象。Spring IOC 容器负责创建、配置和管理这些 Bean 对象的生命周期。Spring IOC 容器会管…