tslib(触摸屏输入设备的轻量级库)的学习、编译及测试记录

news2024/12/24 3:34:48

目录

  • tslib的简介
  • tslib的源码和make及make install后得到的文件下载
  • tslib的主要功能
  • tslib的工作原理
  • tslib的核心组成部分
  • tslib的框架和核心函数分析
    • tslib的框架
    • tslib的核心函数`ts_setup()`的分析(对如何获取设备名和数据处理流程的分析)
      • 函数`ts_setup()`自身的主要代码
      • `ts_setup()`对`ts_open()`的调用
      • `ts_setup()`对`ts_config()`的调用
    • tslib的核心函数`ts_read()`的分析
  • tslib的交叉编译
  • 上板测试

tslib的简介

tslib 是一个用于 Linux 系统中触摸屏输入设备的轻量级库,特别常用于嵌入式开发。它主要提供了一组工具和 API,用于对触摸屏输入事件进行校准、过滤和处理,使得触摸屏可以更精确、更稳定地与系统交互。
应用场景如下:

  • 嵌入式 Linux 设备(如工业控制、车载系统、POS 终端)。
  • 需要轻量级触摸屏处理库的开发环境。
  • 与 Qt、SDL 等图形界面结合使用。

tslib的源码和make及make install后得到的文件下载

tslib-1.21的源码下载地址:
https://pan.baidu.com/s/1Yc1IiRqECn6SyJHI9-6Ksg?pwd=t4zj

附:tslib-1.21make及make install后得出的输入文件下载地址:
https://pan.baidu.com/s/1U6_JUJEiJLAdniZW5wLEoQ?pwd=x4w6

tslib的主要功能

  1. 校准 (Calibration)
    提供 ts_calibrate 工具,可以对触摸屏进行几何校准,解决触摸位置和显示坐标不匹配的问题。

  2. 事件过滤 (Filtering)
    提供了一系列的过滤器,比如去抖动、线性化等,保证输入事件的平滑性和准确性。

  3. 事件捕获 (Event Capture)
    捕获触摸屏输入设备(如 /dev/input/eventX/dev/tsX)的触摸事件,并转化为标准格式供上层应用使用。

  4. 兼容性强
    支持多种触摸屏控制器(例如 eGalax、Goodix 等)和多种 Linux 输入子系统。

tslib的工作原理

  1. 应用程序通过调用 tslib 的 API 读取触摸事件。
  2. tslib 内部从触摸屏设备获取原始事件。
  3. 通过过滤器链对原始事件进行处理(如校准、去抖动)。
  4. 输出处理后的事件数据供应用程序使用。

tslib的核心组成部分

  • 库文件
    提供了核心的触摸屏数据处理和 API,比如 ts_read()ts_config()

  • 工具程序

    • ts_calibrate: 校准工具,用于生成校准数据。
    • ts_test: 用于测试触摸屏事件和显示触摸点。
    • ts_print: 打印触摸事件的工具。
  • 配置文件
    通常是 /etc/ts.conf,可以配置过滤器、设备路径等。

tslib的框架和核心函数分析

tslib的框架

在这里插入图片描述
从上面的框架可以看出,tslib有三个核心函数,分别为ts_setup、ts_read、ts_read_mt。其中ts_setup依靠两个核心函数分别为ts_open和ts_config来实现。

tslib的核心函数ts_setup()的分析(对如何获取设备名和数据处理流程的分析)

函数ts_setup()自身的主要代码

我们打开源码\tslib-1.21\tests\ts_test.c,转到主函数,发现第134有如下代码:

	ts = ts_setup(NULL, 0);

我们转到函数ts_setup()的定义,如下:
位置:\tslib-1.21\src\ts_setup.c

struct tsdev *ts_setup(const char *dev_name, int nonblock)
{
	const char * const *defname;
	struct tsdev *ts = NULL;
#if defined (__linux__)
	char *fname = NULL;
#endif /* __linux__ */

	dev_name = dev_name ? dev_name : getenv("TSLIB_TSDEVICE");

	if (dev_name != NULL) {
		ts = ts_open(dev_name, nonblock);
	} else {
		defname = &ts_name_default[0];
		while (*defname != NULL) {
			ts = ts_open(*defname, nonblock);
			if (ts != NULL)
				break;

			++defname;
		}
	}

#if defined (__linux__)
	if (!ts) {
		fname = scan_devices();
		if (!fname)
			return NULL;

		ts = ts_open(fname, nonblock);
		free(fname);
	}
#endif /* __linux__ */

	/* if detected try to configure it */
	if (ts && ts_config(ts) != 0) {
		ts_error("ts_config: %s\n", strerror(errno));
		ts_close(ts);
		return NULL;
	}

	return ts;
}

我们可以看到它有两个输入参数,一个是dev_name,另一个是nonblock
dev_name显然是表示设备名。
nonblock表示读取数据的时候是阻塞方式还是非阻塞方式,当nonblock的值为1(或非 0)时,表示非阻塞模式,当nonblock的值为0时,为阻塞方式,所以在文件ts_test.c中,是以阻塞方式打开触摸屏设置的,因为给的参数值为0嘛,这意味着从触摸屏读取数据时,如果数据尚未准备好,进程会阻塞,直到数据可用。延伸阅读:Linux系统的阻塞方式和非阻塞方式是什么意思?

在这里,参数dev_name的值传递为NULL,即空指针,那么在函数ts_setup()里有下在这一行:

dev_name = dev_name ? dev_name : getenv("TSLIB_TSDEVICE");

可见,如果有值,则用它本身的值,如果值为NULL,那么 getenv("TSLIB_TSDEVICE") 的结果将作为 dev_name 的值。
所以源码\tslib-1.21\tests\ts_test.c里会去取 getenv("TSLIB_TSDEVICE") 的结果作为设备名。函数getenv()的意思是从环境变量里获取设备名,比如这里会从环境变量TSLIB_TSDEVICE里获取设备名。

那如果既没有传递设备名,也没有设置环境变,函数ts_setup()它会像下面这样做:

static const char * const ts_name_default[] = {
		"/dev/input/ts",
		"/dev/input/touchscreen",
		"/dev/touchscreen/ucb1x00",
		NULL
};
	if (dev_name != NULL) {
		ts = ts_open(dev_name, nonblock);
	} else {
		defname = &ts_name_default[0];
		while (*defname != NULL) {
			ts = ts_open(*defname, nonblock);
			if (ts != NULL)
				break;

			++defname;
		}
	}

即,它会去遍历字符串数组ts_name_default中的各个成员,发现第一个不为NULL值的成员并且能够打开成功的话,即用这个成员作为设备名。

如果到这里还没有成功打开设备,在定义了宏__linux__的情况下,它会去扫描系统中的设备,代码如下:

#if defined (__linux__)
	if (!ts) {
		fname = scan_devices();
		if (!fname)
			return NULL;

		ts = ts_open(fname, nonblock);
		free(fname);
	}
#endif /* __linux__ */

scan_devices()函数中有下面的代码:

ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, alphasort);

DEV_INPUT_EVENT的宏定义如下:

#define DEV_INPUT_EVENT "/dev/input"

即它会去目录/dev/input下去扫描设备。扫描到的设备列表存放在ndev中,然后一个个去判断是设是触摸类型的设备,通过下面这句代码来判断:

		if ((ioctl(fd, EVIOCGPROP(sizeof(propbit)), propbit) < 0) ||
			!(propbit[BIT_WORD(INPUT_PROP_DIRECT)] &
				  BIT_MASK(INPUT_PROP_DIRECT)))

ioctl()函数会获取到设备信息,这段代码通过查询设备属性位 INPUT_PROP_DIRECT,判断设备是否为触摸屏类型设备。如果设备支持 INPUT_PROP_DIRECT 属性,则认为是直接输入设备(如触摸屏)。否则,它可能是间接输入设备或其他设备。具体的这段代码的详解这里就不展开了。

如果扫描到某个设备是触摸型输入设备,它就会去在tslib库的层面上利用函数ts_open()去打开它,具体的代码是下面的代码:

ts_setup()ts_open()的调用

ts = ts_open(fname, nonblock);

可见,核心函数ts_setup()是依靠ts_open()函数实现的。另外,核心函数ts_setup()还要依靠函数ts_config(),那我们来看下是怎么依靠函数ts_config()的。

ts_setup()ts_config()的调用

接下来的代码:

	/* if detected try to configure it */
	if (ts && ts_config(ts) != 0) {
		ts_error("ts_config: %s\n", strerror(errno));
		ts_close(ts);
		return NULL;
	}

可见,设备打开之后,就开始配置,函数ts_config()的定义如下:

int ts_config(struct tsdev *ts)
{
	return __ts_config(ts, NULL, NULL, NULL);
}

可见,它以设备打开获得的结构体ts为输入参数,并对这个结构体ts进行配置。具体的配置是在函数__ts_config()中进行的。函数__ts_config()以“__”开头,说明它是一个内部函数,供库或框架的内部代码调用。

函数__ts_config()的定义如下:

static int __ts_config(struct tsdev *ts, char **conffile_modules,
		       char **conffile_params, int *raw)
{
	char buf[BUF_SIZE], *p;
	FILE *f;
	int line = 0;
	int ret = 0;
	short strdup_allocated = 0;

	char *conffile;

	if ((conffile = getenv("TSLIB_CONFFILE")) == NULL) {
		conffile = strdup(TS_CONF);
		if (conffile) {
			strdup_allocated = 1;
		} else {
			ts_error("Couldn't find tslib config file: %s\n",
				 strerror(errno));
			return -1;
		}
	}

	f = fopen(conffile, "r");
	if (!f) {
		if (strdup_allocated)
			free(conffile);

		ts_error("Couldn't open tslib config file %s: %s\n",
			 conffile, strerror(errno));
		return -1;
	}

	buf[BUF_SIZE - 2] = '\0';
	while ((p = fgets(buf, BUF_SIZE, f)) != NULL) {
		char *e;
		char *tok;
		char *module_name;

		line++;

		/* Chomp */
		e = strchr(p, '\n');
		if (e)
			*e = '\0';

		/* Did we read a whole line? */
		if (buf[BUF_SIZE - 2] != '\0') {
			ts_error("%s: line %d too long\n", conffile, line);
			break;
		}

	#if !defined HAVE_STRSEP
		tok = ts_strsep(&p, " \t");
	#else
		tok = strsep(&p, " \t");
	#endif
		discard_null_tokens(&p, &tok);

		/* Ignore comments or blank lines.
		 * Note: strsep modifies p (see man strsep)
		 */
		if (p == NULL || *tok == '#')
			continue;

		/* Search for the option. */
		if (strcasecmp(tok, "module") == 0) {
		#if !defined HAVE_STRSEP
			module_name = ts_strsep(&p, " \t");
		#else
			module_name = strsep(&p, " \t");
		#endif
			discard_null_tokens(&p, &module_name);
			if (!conffile_modules) {
				ret = ts_load_module(ts, module_name, p);
			} else {
			#ifdef DEBUG
				printf("TSLIB_CONFFILE: module %s %s\n",
					module_name, p);
			#endif
				sprintf(conffile_modules[line], "%s", module_name);
				if (conffile_params)
					sprintf(conffile_params[line], "%s", p);
			}
		} else if (strcasecmp(tok, "module_raw") == 0) {
		#if !defined HAVE_STRSEP
			module_name = ts_strsep(&p, " \t");
		#else
			module_name = strsep(&p, " \t");
		#endif
			discard_null_tokens(&p, &module_name);
			if (!conffile_modules) {
				ret = ts_load_module_raw(ts, module_name, p);
			} else {
			#ifdef DEBUG
				printf("TSLIB_CONFFILE: module_raw %s %s\n",
					module_name, p);
			#endif
				sprintf(conffile_modules[line], "%s", module_name);
				if (conffile_params)
					sprintf(conffile_params[line], "%s", p);

				if (raw)
					raw[line] = 1;
			}
		} else {
			ts_error("%s: Unrecognised option %s:%d:%s\n",
				 conffile, line, tok);
			break;
		}
		if (ret != 0) {
			ts_error("Couldn't load module %s\n", module_name);
			break;
		}
	}

	if (ts->list_raw == NULL) {
		ts_error("No raw modules loaded.\n");
		ret = -1;
	}

	fclose(f);

	if (strdup_allocated)
		free(conffile);

	return ret;
}

从这个代码中我们可以看出,我们可以使用环境变量TSLIB_CONFFILE来确定使用哪个配置文件,相关代码如下:

if ((conffile = getenv("TSLIB_CONFFILE")) == NULL) {

如果环境变量TSLIB_CONFFILE为空,那么就会使用 strdup(TS_CONF)的返回值作为配置文件,这个配置文件其实就是/etc/ts.conf
这里,我们打开tslib源码里配的\tslib-1.21\etc\ts.conf"文件,看一下:
打开它后,把被注释的去掉,没有注释的提取出来,如下:

module_raw input
module pthres pmin=1
module dejitter delta=100
module linear

其中,input、pthres、dejitter、linear都是模块名。pthres会处理input得到的数据、 dejitter会处理pthres得到的数据,linear会处理dejitter得到的数据。具体的逻辑关系可以通过分析函数__ts_config()下面的代码得到:
在这里插入图片描述
从这个关系我们可以看出,当模块类型为module_raw时,调用函数ts_load_module_raw(),当模块类型为module时,调用函数ts_load_module(),这两个被调用的函数分别如下:

int ts_load_module(struct tsdev *ts, const char *module, const char *params)
{
	return __ts_load_module(ts, module, params, 0);
}

int ts_load_module_raw(struct tsdev *ts, const char *module, const char *params)
{
	return __ts_load_module(ts, module, params, 1);
}

可见,它们其实都是调用的__ts_load_module(),只是最后一个参数不一样,一个最后为0,一个最后为1,再接着看__ts_load_module(),它与这个问题相关的核心代码在:

	if (raw)
		ret = __ts_attach_raw(ts, info);
	else
		ret = __ts_attach(ts, info);

这里面raw的值就是其最后一个参数的值,info表示模块信息。
所以我们需要看去函数__ts_attach_raw()__ts_attach()

int __ts_attach(struct tsdev *ts, struct tslib_module_info *info)
{
	info->dev = ts;
	info->next = ts->list;
	ts->list = info;

	return 0;
}

int __ts_attach_raw(struct tsdev *ts, struct tslib_module_info *info)
{
	struct tslib_module_info *next, *prev, *prev_list = ts->list_raw;

	info->dev = ts;
	info->next = prev_list;
	ts->list_raw = info;

	/*
	 * ensure the last item in the normal list now points to the
	 * top of the raw list.
	 */
	if (ts->list == NULL || ts->list == prev_list) {
		/* main list is empty, ensure it points here */
		ts->list = info;
		return 0;
	}

	for (next = ts->list, prev = next;
	     next != NULL && next != prev_list;
	     next = prev->next, prev = next);

	prev->next = info;
	return 0;
}

函数__ts_attach_raw()__ts_attach()的输入参数都有一个关键结构体 tsdev

struct tsdev {
	int fd;
	char *eventpath;
	struct tslib_module_info *list;
	/* points to position in 'list' where raw reads
	 * come from.  default is the position of the
	 * ts_read_raw module.
	 */
	struct tslib_module_info *list_raw;
	unsigned int res_x;
	unsigned int res_y;
	int rotation;
};

可见,这个结构体有两个与模块信息有关的成员,分别为list和list_raw。
当处理第一个模块时,即语句:

module_raw input

时,__ts_attach_raw()会把ts->list_raw的值设为input模块,同时把ts->list的第一个值也设为input模块,然后后面的几个模块配置语句:

module pthres pmin=1
module dejitter delta=100
module linear

由于不是raw类型,所以__ts_attach()去执行相应的链接操作,说白了就是把这些模块按顺序连接起来,形成一个链表,具体来说,在ts->list_raw中存储了module_raw模块的信,而在ts->list则把所有的模块以列表的形式存储起来,示意图如下:
在这里插入图片描述
通过这个示意图可以看出,数据是一层一层地被处理和过滤,这就是tslib对数据的核心处理流程,即通过配置文件去配置对数据的处理流程。

tslib的核心函数ts_read()的分析

我们打开源码\tslib-1.21\tests\ts_test.c,转到主函数,发现第174有如下代码:

		ret = ts_read(ts, &samp, 1);

我们进入函数ts_read(),来看下它的流程:

int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
{
	int result;
#ifdef DEBUG
	int i;
#endif

	result = ts->list->ops->read(ts->list, samp, nr);
#ifdef DEBUG
	for (i = 0; i < result; i++) {
		fprintf(stderr, "TS_READ----> x = %d, y = %d, pressure = %d\n",
			samp->x, samp->y, samp->pressure);

		samp++;
	}
#endif
	return result;

}

关键语如下:

result = ts->list->ops->read(ts->list, samp, nr);

我们可以看到,它是调取linear模块的ops成员的read()成员函数
在这里插入图片描述
然后我们再看下ts->list->ops->read()函数的定义:
这个read()函数实际上是在文件\tslib-1.21\plugins\linear.c中定义的函数linear_read(),然后它作为结构体ops的成员函数,如下面的代码所示:

static const struct tslib_ops linear_ops = {
	.read		= linear_read,
	.read_mt	= linear_read_mt,
	.fini		= linear_fini,
};

在文件\tslib-1.21\plugins\linear.c的第55行,有对函数linear_read()定义的代码如下:

ret = info->next->ops->read(info->next, samp, nr_samples);

我们看到,它实际上是去调用下一个模块中成员ops中的read()函数,实际上就是dejitter模块中的read()函数,以此类推,最终是去调用模块input中的read()函数。
可以推测,模块input中的read()函数是直接去操作设备读取信息了,我们看下是不是这样,打开源文件:\tslib-1.21\plugins\input-raw.c,然后去看里面的函数ts_input_read(),发现关键代码如下:

ret = read(ts->fd, &ev, sizeof(struct input_event));

这个显然就是系统里的符合POSIX标准read()函数了, 关于这个函数的介绍,请参见我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/144645178

tslib的交叉编译

首先把tslib的源码放到目录/home/book/usedlib下:
在这里插入图片描述
然后解压:

tar xjf tslib-1.21.tar

在这里插入图片描述
进入目录:

cd /home/book/usedlib/tslib-1.21

配置编译:

./configure --host=arm-buildroot-linux-gnueabihf --prefix=/

注意:这里一定要根据自己系统中的交叉编译器的前缀来填,可用下面的命令来测试自己的交叉编译器的前缀:

arm-buildroot-linux-gnueabihf-gcc -v

如果有结果返回,则这里--host的值该为arm-buildroot-linux-gnueabihf

在这里插入图片描述
执行make命令:

make

在这里插入图片描述
在这里插入图片描述
安装到当前目录下的子目录tmp中

make install DESTDIR=$PWD/tmp

运行之后就在tmp目录下生成了如下目录:
在这里插入图片描述
其中bin目录中是生成的可执行程序,主要是一些测试实例:
在这里插入图片描述
etc目录里的文件自然就是配置文件了,因为我的前缀设置为根目录(--prefix=/),这个得复制到开发板上的linux系统下的etc目录下,因为根据上面的分析,函数ts_config()在运行时需要使用到这个文件。
在这里插入图片描述

inlude目录里是头文件,如果你在开发别的项目时用到了这个库,那得在你的工程的include目录中放上这个头文件:
在这里插入图片描述
lib目录里就是库文件了,如果你在开发别的项目时用到了这个库,那链接器就需要用到这个库里面的相关文件。
在这里插入图片描述
在这里,我们首先要测试我们生成的二进制可执行文件是否是适用于ARM架构的,因为如果你前边的编译器前缀填错了,它并不会报错,而会去用系统中其它的编译器,比如×86架构的编译器。
运行下面的命令:

file /home/book/usedlib/tslib-1.21/tmp/bin/ts_test_mt

运行结果如下:

/home/book/usedlib/tslib-1.21/tmp/bin/ts_test_mt: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 4.9.0, not stripped

这就说明下面这条命令中,参数 --host的设置是正确的。:

./configure --host=arm-buildroot-linux-gnueabihf --prefix=/

在这里,我们把它复制到目录/usr/local/lib中,并重命为tslib,这样编译器编译别的工程时就能用到这个库了,这里得能命令复制,因为目录/usr/local/lib不是能随便写文件的,运行下面的命令:

sudo cp -rfd /home/book/usedlib/tslib-1.21/tmp/lib /usr/local/lib/tslib

在这里插入图片描述

附:tslib-1.21make及make install后得出的输入文件下载地址:
https://pan.baidu.com/s/1U6_JUJEiJLAdniZW5wLEoQ?pwd=x4w6

上板测试

把文件
/home/book/usedlib/tslib-1.21/tmp/etc/ts.conf
/home/book/usedlib/tslib-1.21/tmp/bin/ts_print_mt
复制到NFS文件中:
在这里插入图片描述

打开开发板的串口终端→打开开发板→挂载网络文件:

mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

复制ts.conf到开发板的ect目录:

cp /mnt/ts.conf /etc/

复制文件ts_print_mt到开发板的/bin目录下:

cp /mnt/ts_print_mt /bin

在这里插入图片描述
为文件ts_print_mt添加执行权限:

chmod +x ts_print_mt

由于ts_print_mt这个文件需要动态库支持,所以我们还需要把生成的动态库复制到对应目录下:

按下面的操作复制库,先把整个make install命令生成的目录tmp复制到NFS文件中:
在这里插入图片描述
然后按下面的命令复制:

cp /mnt/tmp/lib/ts -rfd /lib
cp /mnt/tmp/lib/*so* -fd /lib

其中的参数r表示递归复制,f表示强制覆盖,d表示保留符号链接,而不是把符号链接所指向的文件进行复制。

复制完了运行可执行文件:

/bin/ts_print_mt 

在这里插入图片描述
用手指在触摸屏上划一下:
在这里插入图片描述
这样就完美运行了~

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

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

相关文章

深度学习实战车辆目标跟踪【bytetrack/deepsort】

本文采用YOLOv8作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv8以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对车辆目标数据集进行训练和优化&#xff0c;该数据集包含丰富的车辆目标图像样本…

unipp中使用阿里图标,以及闭坑指南

-----------------------------------------------------点赞收藏才是更新的动力------------------------------------------------- unipp中使用阿里图标 官网下载图标在项目中引入使用注意事项 官网下载图标 进入阿里图标网站 将需要下载的图标添加到购物车中 2. 直接下载…

《Vue3实战教程》5:响应式基础

如果您有疑问&#xff0c;请观看视频教程《Vue3实战教程》 响应式基础​ API 参考 本页和后面很多页面中都分别包含了选项式 API 和组合式 API 的示例代码。现在你选择的是 组合式 API。你可以使用左侧侧边栏顶部的“API 风格偏好”开关在 API 风格之间切换。 声明响应式状态…

【ue5学习笔记2】在场景放入一个物体的蓝图输入事件无效?

在场景放入一个物体的蓝图输入事件无效&#xff0c;那是因为你不知道gameMode这个东西这是一个用于设定游戏股则的东西&#xff0c; 就好比你的控制对象&#xff0c;你输入无效是没有指定你当前关卡中指定的控制对象是它。操作方法如下&#xff1a; 1.创建一个gameMode蓝图类并…

OnlineMusic项目测试报告

OnlineMusic项目测试报告 一、项目背景1.1 测试目标及测试任务的概括1.2 被测的系统&#xff0c;代码以及文档等信息 二、测试安排2.1 测试用例设计2.2 测试方案设计 三、测试分类3.1 测试方案3.1.1 功能测试3.1.2 自动化测试3.1.3 性能测试 3.2测试结果性能测试报告 一、项目背…

Webpack学习笔记(1)

1.为什么使用webpack? webpack不仅可以打包js代码&#xff0c;并且那个且支持es模块化和commonjs,支持其他静态资源打包&#xff0c;如图片、字体。。。 2.如何解决作用域问题&#xff1f; 作用域问题&#xff1a;例如loadsh等库&#xff0c;会绑定window对象&#xff0c;会…

信息安全管理与评估赛题第9套

全国职业院校技能大赛 高等职业教育组 信息安全管理与评估 赛题九 模块一 网络平台搭建与设备安全防护 1 赛项时间 共计180分钟。 2 赛项信息 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 网络平台搭建与设备安全防护 任务1 网络平台搭建 XX:XX- XX:XX 50 任务2…

thinkphp5验证码captcha无法显示

排查思路 是否开启gd2以及gd2排查bom排查代码清除缓存 开启gd/gd2 找到php.ini 开启dg2库 去掉前面的;注释&#xff0c;有的可能会带.dll后缀影响不大 然后通过生成图片验证是否成功 查看是否存在bom 修改为utf-8即可&#xff0c;如果你的代码携带bom也需要排查一下 代码问…

90度Floating B to B 高速连接器信号完整性仿真

在180度 B to B Connector 信号完整性仿真时&#xff0c;不会碰到端口设置不方便问题&#xff0c;但在做90度B to B Connector信号完整性仿真时就会碰到端口设置问题。如下面的90度B to B Connector。 公座 母座 公母对插后如下&#xff1a; 客户要求改Connector需符合PCI-E3.…

ffmpeg翻页转场动效的安装及使用

文章目录 前言一、背景二、选型分析2.1 ffmpeg自带的xfade滤镜2.2 ffmpeg使用GL Transition库2.3 xfade-easing项目三、安装3.1、安装依赖([参考](https://trac.ffmpeg.org/wiki/CompilationGuide/macOS#InstallingdependencieswithHomebrew))3.2、获取ffmpeg源码3.3、融合xf…

用人话讲计算机:Python篇!(十五)迭代器、生成器、装饰器

一、迭代器 &#xff08;1&#xff09;定义 标准解释&#xff1a;迭代器是 Python 中实现了迭代协议的对象&#xff0c;即提供__iter__()和 __next__()方法&#xff0c;任何实现了这两个方法的对象都可以被称为迭代器。 所谓__iter__()&#xff0c;即返回迭代器自身 所谓__…

【计算机视觉基础CV-图像分类】02-入门详解图像分类、经典数据集、比赛与冠军图像模型演进史

前言 图像分类&#xff08;Image Classification&#xff09;是计算机视觉&#xff08;Computer Vision&#xff09;中一项基础且核心的任务。简单来说&#xff0c;就是让计算机从给定的类别集合中&#xff0c;为一张输入图片分配一个正确的类别标签。这个过程听起来直观&…

Docker_常用命令详解

这篇文章分享一下笔者常用的Docker命令供各位读者参考。 为什么要用Docker? 简单来说&#xff1a;Docker通过提供轻量级、隔离且可移植的容器化环境&#xff0c;使得应用在不同平台上保持一致性、易于部署和管理&#xff0c;具体如下 环境一致性&#xff1a; Docker容器使得…

CFA知识点梳理系列:CFA Level II, Reading 4 Big Data Projects

这是CFA知识点梳理系列的第四篇文章&#xff0c;前面的文章可以参考以下链接: CFA知识点梳理系列&#xff1a;CFA Level II, Reading 3 Machine Learning

自制数据库迁移工具-C版-06-HappySunshineV1.5-(支持南大Gbase8a、PostgreSQL、达梦DM)

目录 一、环境信息 二、简述 三、架构图 四、升级点 五、支持功能 六、后续计划支持功能 七、安装包下载地址 八、配置参数介绍 九、安装步骤 1、用户创建 2、安装包解压 3、环境变量配置 4、环境变量生效 5、动态库链接检验 &#xff08;1&#xff09;HsManage…

petalinux-adi ---移植adi内核(一)

1. 设备树生成 将 前 面 生 成 的 设 备 树 文 件 ( 笔 者 这 里 生 成 的 设 备 树 文 件 在Petalinux 工 程 的components/plnx_workspace/device-tree/device-tree/ 目 录 下 ) pcw.dtsi 、 pl.dtsi 、system-top.dts 以 及 zynq-7000.dtsi 四 个 文 件 直 接 拷 贝 到 内 …

从腾讯云的恶意文件查杀学习下PHP的eval函数

问题来自于腾讯云的主机安全通知&#xff1a; &#x1f680;一键接入&#xff0c;畅享GPT及AI大模型服务&#xff01;【顶级API中转品牌】&#xff1a; https://api.ablai.top/ 病毒文件副本内容如下&#xff1a; <?php function x($x){eval($x);}x(str_rot13(riny($_CBF…

Tool之Excalidraw:Excalidraw(开源的虚拟手绘风格白板)的简介、安装和使用方法、艾米莉应用之详细攻略

Tool之Excalidraw&#xff1a;Excalidraw(开源的虚拟手绘风格白板)的简介、安装和使用方法、艾米莉应用之详细攻略 目录 Excalidraw 简介 1、Excalidraw 的主要特点&#xff1a; Excalidraw 安装和使用方法 1、Excalidraw的安装 T1、使用 npm 安装&#xff1a; T2、使用 …

LLMs之rStar:《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读

LLMs之rStar&#xff1a;《Mutual Reasoning Makes Smaller LLMs Stronger Problem-Solvers》翻译与解读 导读&#xff1a;这篇论文提出了一种名为rStar的自我博弈互推理方法&#xff0c;用于增强小型语言模型 (SLMs) 的推理能力&#xff0c;无需微调或依赖更强大的模型。rStar…

Solidity 智能合约安全漏洞——普通重入攻击

普通重入攻击 重入攻击&#xff08;Re-Entrancy&#xff09; 一直是以太坊智能合约中最危险的漏洞之一&#xff0c;导致了许多大规模的资金被盗事件。比如 2016 年发生在 The DAO 项目中的 Re-Entrancy 漏洞攻击&#xff0c;造成价值当时 6000 万美元的以太币被盗&#xff0c;…