龙云尧个人博客,转载请注明出处。
CSDN地址:http://blog.csdn.net/michael753951/article/details/72810534
个人blog地址:http://yaoyl.cn/nehexue-xi-bi-ji-shi/
概述
本部分博客将以nehe教程第2课,笔记(三)为蓝本,将Windows中完成的基础实验在Ubuntu中进行实现。
在【Ubuntu环境配置】中我们已经对Ubuntu中的OpenGL环境进行了配置,并且完成了最基础的茶壶demo,接下来我们将进行实验相关的后续开发。
需求分析
因为实验中我们需要终端接收到的数据能够在图形界面中实时显示出来,这里我们使用nehe教程的第二课内容,绘制一个矩形作为进度条,起始为0%,最高为100%。接着我们将让这个进度条能够对传输过来的信号产生反馈。将整个过程进行拆分,我们可以按照如下步骤进行实现。
- 构建一个OpenGL窗口,能够根据本地按键实现进度条控制
- 让OpenGL的窗口能够接收其他终端发送过来的消息
- 让OpenGL窗口对接收到的信息进行一定的实时反馈(比如进度条变换)
实验
OpenGL窗口搭建
本次使用的代码是以nehe教程第二课中,Linux代码为蓝本,进行修改实现的。【代码链接】
首先我们将窗口显示中的三角形去掉,留下一个长方形,同时将长方形的右边两个点和左边两个点重合以做出进度条为0%的感觉。代码如下:
按钮控制的实现
我在初始化InitGL的时候,将square_len初始化为0,当有按键触发的时候,square_len++,这样就能够完成进度条的前进工作。
在原始代码中,我们可以看到main函数中,有一个glutKeyboardFunc
方法,传入了keyPressed
的地址,在keyPressed
中,定义了使用ESC
按钮进行退出的方法。我们将在这里进行尝试,试试方向键左和方向键右能不能让窗口出现一些反馈。
在经过不短的一段时间的寻找之后,我终于找到了在OpenGL中,各种按键的键值是在glut.h
中预定义好的。
参考一片CSDN博客【pengl键盘控制一】,我们可以发现在本次程序中,ESC按键确实也刚好是27,这是不是也就意味着我们可以直接按照上面的方法进行修改了?首先我们将ESC的宏定义值修改为102(十进制,对应0x66),尝试使用左键退出窗体程序。
但是很意外的,没有成功。是不是按键本身的键值并不是102?
我对代码进行进一步修改,当有按键活动的时候,记录下来当前按键的键值,将其存进本地文件中。(亲测不能直接printf,因为根本不会显示出来,至于原因待会会有解释)代码如下:
/* The function called whenever a key is pressed. */
void keyPressed(unsigned char key, int x, int y) {
/* avoid thrashing this procedure */
//usleep(100);
fp = fopen("key_value.txt", "a+"); // a+意味着在文本最后追加
fprintf(fp, "%d\n", key);
fclose(fp);
/* If escape is pressed, kill everything. */
if (key == ESCAPE) {
/* shut down our window */
glutDestroyWindow(window);
/* exit the program...normal termination. */
exit(0);
}
}
尝试按下F1~F12的按键,以及上下左右等按键,以及数字按键之后,我们发现txt文档中只记录下来了数字键值,根本没有其他的键值。
为了解决这个问题,我特地打开了nehe的lesson10的linux代码(因为这一课会用到方向键进行控制)。发现原来上下左右这类按键需要在main函数中使用glutSpecialFunc
方法,传入一个操作函数进行操作。这里我定义了一个specialKeyPressed
方法。在尝试获取键值,并且成功之后,我开始在这里进行进度条的控制。
void specialKeyPressed(int key, int x, int y) {
//usleep(100);
/*
fp = fopen("key_value.txt", "a+");
fprintf(fp, "%d\n", key);
fclose(fp);
*/
switch(key) {
case GLUT_KEY_LEFT:
square_len--;
if(square_len <= 0) square_len = 0;
break;
case GLUT_KEY_RIGHT:
square_len++;
if(square_len >= 100) square_len = 100;
break;
}
}
为了避免越界,我们需要将square_len控制在0-100之间。同时我们直接使用glut中宏定义的键值,进行按键判断(我已经对键值进行过测试。发现和宏定义的键值确实一致)。
到这里,我们完成了本次demo的step1,一个使用按键进行进度条控制的OpenGL窗口已经构建成功。
在OpenGL创建的控制台窗口中使用网络协议传输
首先,我们需要知道,在之前的Socket编程中,我们使用的一直都是控制台窗口程序进行的测试,但是在本次实验中,我的理想状态是使用OpenGL建立的窗口作为server,新建一个控制台作为client,然后实验中使用client对server进行控制。
我先定义了一个tcp_server.h头文件。
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#define MYPORT 8887
#define QUEUE 20
bool tcp_server_init(int &server_sockfd, int &conn) {
///定义sockfd
server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
///定义sockaddr_in
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(MYPORT);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
///bind,成功返回0,出错返回-1
if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1) {
perror("bind");
return 0;
}
///listen,成功返回0,出错返回-1
if(listen(server_sockfd,QUEUE) == -1) {
perror("listen");
return 0;
}
///客户端套接字
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
///成功返回非负描述字,出错返回-1
conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);
if(conn<0) {
perror("connect");