【读书笔记-《30天自制操作系统》-23】Day24

本篇内容依然比较简单,主要是优化窗口功能以及开发定时器应用程序。首先是优化窗口的切换功能,实现通过键盘和鼠标切换窗口,然后是实现通过鼠标关闭窗口。接着实现不同窗口输入状态的切换,最后是实现定时器的API与应用程序。
在这里插入图片描述

1. 窗口优化

前面已经完成了窗口的基本功能,现在需要对窗口功能进行进一步优化。首先是增加窗口的切换功能。

1.1 通过按键实现窗口切换

先从简单的开始,实现按下F11后将最下面的窗口放在最上面。F11的按键编码为0x57,只需要在主程序中添加如下代码:

……
if (i == 256 + 0x57 && shtctl->top > 2) 
{	/* F11 */sheet_updown(shtctl->sheets[1], shtctl->top - 1);
}
……

代码很简单,sheets[0]表示背景图层,sheets[1]即为最下层的窗口。top图层为鼠标图层,按下F11键后将最下层的窗口调整到鼠标图层下一层即可(因为不能覆盖鼠标)。
在这里插入图片描述
1.2 通过鼠标实现窗口切换

接下来实现通过鼠标点击实现的窗口切换。鼠标点击画面上的某个地方时,我们需要按照从上到下的顺序判断鼠标的位置落在哪个图层的范围内,并且还需要确保该位置不是透明色区域:

else if (512 <= i && i <= 767) 
{ /* 鼠标数据 */if (mouse_decode(&mdec, i - 512) != 0) {/* 鼠标指针移动 */mx += mdec.x;my += mdec.y;if (mx < 0) {mx = 0;}if (my < 0) {my = 0;}if (mx > binfo->scrnx - 1) {mx = binfo->scrnx - 1;}if (my > binfo->scrny - 1) {my = binfo->scrny - 1;}sheet_slide(sht_mouse, mx, my);if ((mdec.btn & 0x01) != 0) {/* 按下左键 *//* 按照从上到下的顺序寻找鼠标所指向的图层 */for (j = shtctl->top - 1; j > 0; j--) {sht = shtctl->sheets[j];x = mx - sht->vx0;y = my - sht->vy0;if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {sheet_updown(sht, shtctl->top - 1);break;}}}}}

这一部分的逻辑也比较清楚。按照从上到下的顺序寻找鼠标点击的图层,如果鼠标点击的位置处于该图层的范围之内,就将该图层移动到最上面。如果将命令行窗口移动到最上面,则会遮挡其他窗口。不过这时又可以通过F11将下面的窗口切换上来。
在这里插入图片描述

1.3 窗口移动

窗口的切换功能基本完成了,接下来实现窗口的移动功能。之前单独实现了任务A窗口的实现功能,现在有了多个任务窗口,需要重新进行实现。

当鼠标左键点击窗口时,如果点击窗口的标题栏区域,则进入窗口移动模式,使窗口位置随着鼠标指针移动;而放开鼠标左键时,退出窗口移动模式,返回普通模式。实现窗口的移动需要记录鼠标移动的距离,这里添加了两个变量mmx和mmy,用于记录移动之前的坐标,并且规定mmx=-1时不处于窗口移动模式。

……
int j, x, y, mmx = -1, mmy = -1;
struct SHEET *sht = 0;else if (512 <= i && i <= 767) 
{ /* 鼠标数据 */if (mouse_decode(&mdec, i - 512) != 0) {/*按下左键 */mx += mdec.x;my += mdec.y;if (mx < 0) {mx = 0;}if (my < 0) {my = 0;}if (mx > binfo->scrnx - 1) {mx = binfo->scrnx - 1;}if (my > binfo->scrny - 1) {my = binfo->scrny - 1;}sheet_slide(sht_mouse, mx, my);if ((mdec.btn & 0x01) != 0) {/* 按下左键 */if (mmx < 0) {/* 处于通常模式,不处于窗口移动模式 *//* 按照从上到下的顺序寻找鼠标指针所在的图层 */for (j = shtctl->top - 1; j > 0; j--) {sht = shtctl->sheets[j];x = mx - sht->vx0;y = my - sht->vy0;if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {sheet_updown(sht, shtctl->top - 1);if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {mmx = mx;	/* 进入窗口移动模式 */mmy = my;}break;}}}} else {/* 如果处于窗口移动模式 */x = mx - mmx;	/* 计算鼠标移动距离 */y = my - mmy;sheet_slide(sht, sht->vx0 + x, sht->vy0 + y);mmx = mx;	/* 更新移动后窗口坐标*/mmy = my;}} else {/* 没有按下左键 */mmx = -1;	/* 返回通常模式 */}}
}

虽然代码长了一些,但基本没有什么新东西,主要还是利用之前已经实现的内容与熟悉的方法。这样就可以通过鼠标移动窗口,更有操作系统的样子了。

在这里插入图片描述

1.4 用鼠标关闭窗口

有了前面用鼠标切换窗口的基础,用鼠标关闭窗口的实现也就顺理成章了。原理基本一致,只是需要增加判断鼠标点击的位置范围是否在窗口右上角的"X"号,并根据点击结束程序就可以了。

……
if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) 
{if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {sheet_updown(sht, shtctl->top - 1);if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {mmx = mx;	mmy = my;}if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {/* 点击"X"按钮 */if (sht->task != 0) {	/* 该窗口是否为应用程序窗口 */cons = (struct CONSOLE *) *((int *) 0x0fec);cons_putstr0(cons, "\nBreak(mouse) :\n");io_cli();	/* 强制结束处理时禁止切换任务 */task_cons->tss.eax = (int) &(task_cons->tss.esp0);task_cons->tss.eip = (int) asm_end_app;io_sti();}}break;}
}
……

在这里插入图片描述
在这里插入图片描述

1.5 切换输入到应用窗口

现在有了多个应用程序窗口,并且像walk这样的应用程序已经可以接受键盘输入了。应该使应用程序的窗口能够切换到输入状态,然后通过键盘进行输入。这里仍然通过tab键来切换多个应用程序窗口,使用key_win变量保存当前处于输入状态的窗口地址。另外如果应用程序的窗口处于输入状态时被关闭,这是就让操作系统自动切换到最上层的窗口。

if (256 <= i && i <= 511) 
{ /* 键盘数据 */
……if (s[0] != 0) { /* 一般字符 */if (key_win == sht_win) {	/* 发送给任务A */if (cursor_x < 128) {/* 显示一个字符,光标后移一位 */s[1] = 0;putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);cursor_x += 8;}} else {	/* 发送至命令行窗口 */fifo32_put(&key_win->task->fifo, s[0] + 256);}}if (i == 256 + 0x0e) {	/* BackSpace键 */if (key_win == sht_win) {	/*  发送给任务A */if (cursor_x > 8) {/* 空格擦除光标后后移一位 */putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);cursor_x -= 8;}} else {	/* 发送至命令行窗口 */fifo32_put(&key_win->task->fifo, 8 + 256);}}if (i == 256 + 0x1c) {	/* Enter */if (key_win != sht_win) {	/* 发送至任务A */fifo32_put(&key_win->task->fifo, 10 + 256);}}if (i == 256 + 0x0f) {	/* Tab */cursor_c = keywin_off(key_win, sht_win, cursor_c, cursor_x);j = key_win->height - 1;if (j == 0) {j = shtctl->top - 1;}key_win = shtctl->sheets[j];cursor_c = keywin_on(key_win, sht_win, cursor_c);}
……
}

key_winon与key_winoff两个函数用于控制串口标题栏的颜色与任务A窗口的光标:

int keywin_off(struct SHEET *key_win, struct SHEET *sht_win, int cur_c, int cur_x)
{change_wtitle8(key_win, 0);if (key_win == sht_win) {cur_c = -1; /* 删除光标 */boxfill8(sht_win->buf, sht_win->bxsize, COL8_FFFFFF, cur_x, 28, cur_x + 7, 43);} else {if ((key_win->flags & 0x20) != 0) {fifo32_put(&key_win->task->fifo, 3); /* 命令行窗口光标OFF */}}return cur_c;
}int keywin_on(struct SHEET *key_win, struct SHEET *sht_win, int cur_c)
{change_wtitle8(key_win, 1);if (key_win == sht_win) {cur_c = COL8_000000; /* 显示光标 */} else {if ((key_win->flags & 0x20) != 0) {fifo32_put(&key_win->task->fifo, 2); /* 命令行窗口光标ON */}}return cur_c;
}

通过tab键可以实现窗口输入的切换:
在这里插入图片描述

1.6 鼠标切换输入窗口

实现鼠标的窗口输入切换,只需要再增加一点改动:

……
if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) 
{if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {sheet_updown(sht, shtctl->top - 1);/*鼠标点击的图层不是当前输入的图层,切换*/if (sht != key_win) {cursor_c = keywin_off(key_win, sht_win, cursor_c, cursor_x);key_win = sht;cursor_c = keywin_on(key_win, sht_win, cursor_c);}if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {mmx = mx;	mmy = my;}
……}
}

2. 定时器API与应用程序

2.1 定时器应用程序

为了编写定时器的应用程序,先编写了定时器的API:

_api_alloctimer:	; int api_alloctimer(void);MOV		EDX,16INT		0x40RET_api_inittimer:		; void api_inittimer(int timer, int data);PUSH	EBXMOV		EDX,17MOV		EBX,[ESP+ 8]		; timerMOV		EAX,[ESP+12]		; dataINT		0x40POP		EBXRET_api_settimer:		; void api_settimer(int timer, int time);PUSH	EBXMOV		EDX,18MOV		EBX,[ESP+ 8]		; timerMOV		EAX,[ESP+12]		; timeINT		0x40POP		EBXRET_api_freetimer:		; void api_freetimer(int timer);PUSH	EBXMOV		EDX,19MOV		EBX,[ESP+ 8]		; timerINT		0x40POP		EBXRET
……
else if (edx == 16) 
{reg[7] = (int) timer_alloc();
} 
else if (edx == 17) 
{timer_init((struct TIMER *) ebx, &task->fifo, eax + 256);
} 
else if (edx == 18) 
{timer_settime((struct TIMER *) ebx, eax);
} 
else if (edx == 19) 
{timer_free((struct TIMER *) ebx);
}
……

分别实现了获取定时器、设置定时器的发送数据、设置定时器时间、释放定时器的API。应用程序如下:

#include <stdio.h>int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_initmalloc(void);
char *api_malloc(int size);
int api_getkey(int mode);
int api_alloctimer(void);
void api_inittimer(int timer, int data);
void api_settimer(int timer, int time);
void api_end(void);void HariMain(void)
{char *buf, s[12];int win, timer, sec = 0, min = 0, hou = 0;api_initmalloc();buf = api_malloc(150 * 50);win = api_openwin(buf, 150, 50, -1, "noodle");timer = api_alloctimer();api_inittimer(timer, 128);for (;;) {sprintf(s, "%5d:%02d:%02d", hou, min, sec);api_boxfilwin(win, 28, 27, 115, 41, 7 /* 白色*/);api_putstrwin(win, 28, 27, 0 /* 黑色 */, 11, s);api_settimer(timer, 100);	/* 1秒 */if (api_getkey(1) != 128) {break;}sec++;if (sec == 60) {sec = 0;min++;if (min == 60) {min = 0;hou++;}}}api_end();
}

实现的效果就是显示时间:
在这里插入图片描述

2.2 取消定时器

定时器超时时,会发送设置好的数据。但如果定时器超时之前应用程序已经退出了,定时器超时时会产生什么效果呢?
运行定时器程序,关闭应用程序。过1秒钟后(产生超时),命令行窗口中会出现一个异常字符。

在这里插入图片描述

为了解决这种异常的情况,我们需要再应用程序关闭后取消定时器。

首先编写用于取消指定定时器的函数:

int timer_cancel(struct TIMER *timer)
{int e;struct TIMER *t;e = io_load_eflags();io_cli();	/*设置过程中禁止改变定时器状态 */if (timer->flags == TIMER_FLAGS_USING) {	/* 是否需要取消? */if (timer == timerctl.t0) {/* 取消第一个定时器的处理 */t = timer->next;timerctl.t0 = t;timerctl.next = t->timeout;} else {/* 非第一个定时器的取消处理 *//* 找到timer前的一个定时器 */t = timerctl.t0;for (;;) {if (t->next == timer) {break;}t = t->next;}t->next = timer->next; }timer->flags = TIMER_FLAGS_ALLOC;io_store_eflags(e);return 1;	/* 取消处理成功 */}io_store_eflags(e);return 0; /* 不需要取消处理 */
}

在此基础上,再来编写应用结束时取消定时器的函数。同时需要在定时器上增加一个标记,防止命令行窗口中的光标定时器也被取消:

struct TIMER {struct TIMER *next;unsigned int timeout;char flags, flags2;// 增加标志位struct FIFO32 *fifo;int data;
};

通常情况下将flags2置为0:

struct TIMER *timer_alloc(void)
{int i;for (i = 0; i < MAX_TIMER; i++) {if (timerctl.timers0[i].flags == 0) {timerctl.timers0[i].flags = TIMER_FLAGS_ALLOC;timerctl.timers0[i].flags2 = 0;return &timerctl.timers0[i];}}return 0; 
}

对于应用程序申请的定时器,将flags2置为1:

else if (edx == 16) 
{reg[7] = (int) timer_alloc();((struct TIMER *) reg[7])->flags2 = 1;	
}

这样就可以在程序结束取消不需要的定时器了:

void timer_cancelall(struct FIFO32 *fifo)
{int e, i;struct TIMER *t;e = io_load_eflags();io_cli();	for (i = 0; i < MAX_TIMER; i++) {t = &timerctl.timers0[i];if (t->flags != 0 && t->flags2 != 0 && t->fifo == fifo) {timer_cancel(t);timer_free(t);}}io_store_eflags(e);return;
}

运行定时器程序并关闭,超时也不会再出现异常字符了。
在这里插入图片描述
本篇的内容逻辑清楚,代码简单,但却使操作系统看起来更像样了。下一篇继续优化命令行窗口,敬请期待。

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

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

相关文章

Windows Server2016多用户登录破解

使用场景 很多时候&#xff0c;公司开发和测试运维会同时登录同一台windows服务器进行查询、更新、维护等操作&#xff0c;本文就来介绍一下Windows2016配置多人远程桌面登录实现&#xff0c;感兴趣的可以了解一下。 操作流程 &#xff08;1&#xff09;首先桌面需要安装远程…

旅行社区应该如何规划?

近年来&#xff0c;旅游行业逐渐恢复&#xff0c;包括微度假、精致露营、康养旅游、乡村民宿等旅游模式。用户旅游支出、旅游人次逐渐恢复&#xff0c;旅游收入仍待提升。 那么旅游社区应该如何搭建&#xff0c;内容如何规划呢&#xff1f; 我们了解到&#xff0c;很多旅游网…

kettle 数据库迁移 使用分页原理实现 数据库mysql

使用 kettle 9.0 先修改配置文件: C:\Users\xx\.kettle 新增如下配置,解决mysql 空字符串 自动转 null bug KETTLE_EMPTY_STRING_DIFFERS_FROM_NULLY git地址: GitHub - 2292011451/kettle_tool 第一步: 先把要迁移的表进行读取,循环查询每个表的最大数量以及页数,追加到…

ROS 设置dhcp option 6 多个地址格式

ROS routeOS 手工设置 dhcp 服务 option 6 多个dns 地址格式。字符串方式

机器学习(西瓜书)第 14 章 概率图模型

14.1 隐马尔可夫模型 机器学习最重要的任务&#xff0c;是根据一些已观察到的证据&#xff08;例如训练样本&#xff09;来对感兴趣的未知变量&#xff08;例如类别标记&#xff09;进行估计和推测。概率模型&#xff08;probabilistic model&#xff09;提供了一种描述框架&a…

动态线程池(四)

动态线程池 dtp生命周期管理 生命周期相关类图 DtpExecutor EagerEtpExecutor OrderedDtpExecutor TaskWrapper任务包装器 MdcRunnable TaskWrappers NotifyEnum NoticeManager通知管理器 InvokerChain调用链

【CPP】类与继承

14 类与继承 在前面我们提到过继承的一些概念,现在我们来回顾一下 打个比方:在CS2中我们把玩家定义为一个类 class 玩家: 血量:100阵营(未分配)服饰(未分配)位置(未分配)武器(未分配)是否允许携带C4(未分配)是否拥有C4(未分配) 当对局创建时,会新生成两个类,这两个类继承自&qu…

JS实现树形结构数据中特定节点及其子节点显示属性设置的技巧(可用于树形节点过滤筛选)

大家好&#xff0c;今天我要分享的是如何在树形结构的数据中&#xff0c;根据特定条件设置节点及其所有子节点的显示属性。在实际项目中&#xff0c;这种需求非常常见&#xff0c;特别是在需要动态展示和隐藏节点的情况下。下面我将通过一个具体的示例来讲解实现过程。 需求分析…

有关C# .NET Core 过滤器的使用

想用一个过滤器实现特定接口的审核日志记录&#xff0c;结果报了错&#xff0c;看了看感觉有些基础要补&#xff0c;所以想记录下来 错误&#xff1a; 在属性过滤器中使用了依赖注入&#xff0c;结果在应用在控制层接口时报了传参的错 //过滤器 public class AuditRecordFil…

tcpdump使用方法

一、centos上可以采用下面的命令进行安装。 yum install tcpdump 二、实例&#xff1a; 1、监视指定网络接口的数据包 即监听指定网卡的数据包&#xff0c;若不指定网卡&#xff0c;默认tcpdump只会监视第一个网络接口。如监听 eth0网卡&#xff0c;如下&#xff1a; tcpd…

Double-Fetch漏洞检测工具的部署、使用与原理分析

文章目录 前言1、概述1.1、简介1.2、工作原理1.2.1、内核空间与用户空间的信息传递1.2.2、Double-Fetch漏洞产生的原因1.2.3、产生Double-Fetch漏洞的情况1.2.4、一个Double-Fetch漏洞示例1.2.5、Double-Fetch漏洞检测工具原理 1.3、模式匹配原理分析1.3.1、Coccinelle介绍1.3.…

大厂程序员的健身之路

大厂程序员的健身之路 基本信息饮食正餐营养补剂 睡眠训练计划 基本信息 健身时间&#xff1a;2023.03 -> 2024.09体重变化&#xff1a;52kg -> 67kg 饮食 正餐 早餐&#xff1a;不吃午餐&#xff1a;两碗米饭 鱼/鸡肉 蔬菜 酸奶晚餐&#xff1a;两碗米饭 鱼/鸡肉…

java之杨辉三角问题

给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 如何实现呢&#xff1f; 思路&#xff1a;首先&#xff0c;我们可以将杨辉三角视作i行j列的二维数组。除了第一行和第二行之外&am…

BuripSuiteProfessional 抓取HTTPS配置

1.电脑拿开代理 谷歌为例 点击三点-设置 -输入代理--点击代理设置 打开手动代理---IP ,端口如图-点击保存 2.下载CA证书 打开代理后,谷歌浏览器打开,输入/burp--如下图-点击CA证书下载证书 选择下载目录--桌面 3.安装CA证书 谷歌浏览器中点开设置-输入证书-点击安全 点击…

传知代码-多示例AI模型实现病理图像分类

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 本文将基于多示例深度学习EPLA模型实现对乳腺癌数据集BreaKHis_v1的分类。EPLA模型是处理组织病理学图像的经典之作。EPLA模型是基于多示例学习来进行了&#xff0c;那么多示例学习模型对处理病理学图像具有…

优化算法(一)—遗传算法(Genetic Algorithm)附MATLAB程序

遗传算法&#xff08;Genetic Algorithm, GA&#xff09;是一种启发式搜索算法&#xff0c;用于寻找复杂优化问题的近似解。它模拟了自然选择和遗传学中的进化过程&#xff0c;主要用于解决那些传统算法难以处理的问题。 遗传算法的基本步骤&#xff1a; 初始化种群&#xff0…

GNN-RAG:用于大模型推理的图神经检索

GNN-RAG&#xff1a;用于大模型推理的图神经检索 秒懂大纲提出背景解法拆解全流程优化创意总结 论文&#xff1a;GNN-RAG: Graph Neural Retrieval for Large Language Model Reasoning 代码&#xff1a;https://github.com/cmavro/GNN-RAG 秒懂大纲 ├── GNN-RAG【主题】…

美国站群服务器优化技巧解析

美国站群服务器&#xff0c;作为专为管理多个网站而设计的托管解决方案&#xff0c;其优化对于提升网站性能和用户体验至关重要。以下是一些关键的优化技巧&#xff1a; 首先&#xff0c;硬件配置是基础。选择高性能的CPU、大容量的内存以及高速的硬盘(如SSD)是提升服务器运算速…

Java 集合详解

目录 一. 概述 二. Collection接口实现类 三. Map接口实现类 四. 线程安全集合 五. List接口下集合实现原理 1. ArrayList实现原理 1.1. 基于动态数组 1.2. 随机访问 1.3. 添加元素 1.4. 删除元素 1.5. 迭代器 1.6. 克隆和序列化 1.7. ArrayList简单使用 2. Link…

Linux环境变量进程地址空间

目录 一、初步认识环境变量 1.1常见的环境变量 1.2环境变量的基本概念 二、命令行参数 2.1通过命令行参数获取环境变量 2.2本地变量和内建命令 2.3环境变量的获取 三、进程地址空间 3.1进程&#xff08;虚拟&#xff09;地址空间的引入 3.2进程地址空间的布局和理解 …