30天自制操作系统——第11天实验总结

实验日期 实验项目
2020.12.10 第11天 制作窗口

一、问题回答

1、 添加一个窗口图层有哪些步骤?(格式:文字说明+对应代码)

  • 制作绘制窗口的函数,包括大小,颜色,图案等。
    在这里插入图片描述
    在这里插入图片描述
  • 创建管理图层的结构体shtctl和所需的窗口图层,定义窗口图层的缓冲区数组buf_win, 数组主要用来存储窗口图层的画面的像素点。

在这里插入图片描述

  • 初始化图层管理结构体,并为所创建的窗口图层分配一张未被使用的图层。
    在这里插入图片描述
  • 初始化窗口图层相关属性,包括为缓冲区数组分配内存,设置窗口的透明色和大小,和窗口图层缓冲区数组像素的初始化。
    在这里插入图片描述
  • 调用sheet_slide函数移动窗口图层,确定窗口图层最初的位置
    在这里插入图片描述
  • 调用sheet_updown函数,设置窗口图层的高度。需要注意使鼠标图层位于最高层。
    在这里插入图片描述

以上就是创建一个窗口图层的基本步骤

2、 教材202页,为什么鼠标移动到最右边后左边会出现鼠标图案?

在这里插入图片描述
结合代码,当鼠标超过右边界时,将边界的值直接赋值给鼠标左上角的坐标,让鼠标可以隐藏在右边。分析如下:图层移动时,会调用sheet_slide函数,这个给函数内部调用sheet_refreshsub函数对指定范围刷新。而鼠标的坐标经过修正,老图层的起始位置在合理范围内,在sheet_refreshsub函数中,根据像素点的计算赋值(给一维数组赋值),刷新后的像素点后往后移动16,使用一维数组存储二维数据,超过右边的数据会被放到对应下一行的位置,所以刷新的时候就会在左边显示处鼠标的图案。

3、 教材216页,每个图层的sid是如何设置的?具体数值等于多少?举例说明,建议编程打印sid进行验证。

sid是在sheet_refreshsub函数中使用到的号码,其值设置为每个图层对于sheet0的相对大小,即用当前图层的地址减去初始图层的地址。根据计算公式,其大小和图层的高度无关,只和alloc图层时的顺序有关。举个例子,这里展示4个图层,图层分配时是按照sheet0-sheet3的顺序进行分配的,各个图层和sid关系如下表所示

sheet3 sid=3
sheet2 sid=2
sheet1 sid=1
sheet0 sid=0

编程打印:考虑到sid的设置是在refreshsub函数中,如果通过在将函数内部临时变量sid的值打印出来,需要修改很多函数。这里我直接在HariMain函数中,根据计算公式编写程序打印。编写代码如下:
在这里插入图片描述
在这里插入图片描述
观察输出显示结果,分配图层时,最先分配背景图层,sid为0,接着分配鼠标图层,sid为1,最后分配窗口图层,sid为2。改变上述的图层分配顺序,按照鼠标图层,窗口图层,背景图层的顺序进行分配,打印处的sid分别为2 1 0,结果如下所示:
在这里插入图片描述

4、 教材216-217页,结合代码,解释刷新函数(sheet_refreshsub)的参数和实现逻辑。

(1). sheet_refreshsub函数的参数解释
sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)

参数ctl是管理图层的结构体,参数vx0,vy0,vx1,vy1是指定刷新的区域,这里的坐标是相对于整个画面的坐标,参数h0是需要刷新的最低图层的高度,参数h1是需要刷新的最高图层的高度。

(2). 实现逻辑
refreshsub函数

  • 第1步,检查刷新范围的合法性(是否在画面的大小范围内),如果超过了这个范围就必须对其进行修正。

在这里插入图片描述

  • 第2步,从高度为h0的图层开始,到高度为h1的图层结束,依次刷新每个图层。在刷新之前,计算图层sid,并利用vx0vy1对bx0by1进行倒推,注意得到bx0~by1后对其表示的范围修正(第10天内容)。刷新时,使用倒推得到的bx0,by0,bx1,by1和二重for循环对图层的像素点修改。只有当前sid和map中该像素处的sid相等时,才能将缓存数组中的像素写入VRAM中。

在这里插入图片描述

5、 教材217页,结合代码,解释滑动函数(sheet_slide)的参数和实现逻辑,注意内部调用sheet_refreshmap和sheet_refreshsub时的传参,特别是高度参数,为什么这样传。

(1). sheet_slide函数的参数解释
sheet_slide(struct SHEET *sht, int vx0, int vy0)

参数sht是需要移动位置的图层,vx0,vy0是移动后图层左上角的坐标。

(2). 实现逻辑

  • 第1步,保存移动前图层左上角的坐标,并将移动后图层左上角的坐标vx0,vy0赋值给sht->vx0和sht->vy0。

在这里插入图片描述

  • 第2步,从最底下的图层开始绘制移动前图层覆盖区域的地图,接着从当前图层开始绘制移动后图层位置的地图。移动图层后,由于无法判断图层移动后的情况,需要对移动图层下面的所有图层和上面的所有图层进行刷新。

在这里插入图片描述
(3). 传参说明

old_vx0,old_vy0是移动前图层左上角的坐标,vx0,vy0是移动后图层左上角的坐标。sheet_refreshmap函数中第1个参数是图层管理结构体,第2~5个参数值图层左上角和右下角的坐标,右下角的坐标由左上角的坐标分别加上图层的大小的长和宽得到,第6个参数是开始绘制地图的起始图层。

第1个sheet_refreshmap的2~5个参数是传递移动前图层位置,第6个参数是0,表示从最底下的图层开始绘制地图,这是因为移动图层后,下面所有图层是否被遮盖的情况不确定,需要重新开始刷新,对应第1个sheet_refreshsub函数,从高度0开始的图层刷新到高度为sht->height-1的图层。

第2个sheet_refreshmap的2~5个参数是传递移动后图层的位置,第6个参数是sht->height,表示从当前图层开始绘制地图,对应第2个sheet_refreshsub函数,从高度为sht->height的图层到高度为sht->height的图层,这是因为移动图层后对上方的所有图层没有影响。 或者可以理解为移动后的图层地图只是在移动后图层的高度以上进行了刷新,而移动后的图层高度的地图已经绘制好了,直接按照地图刷新当前这一高度的图层就可以了。

所以这样传的原因是,只绘制中间变化的图层,不需要重新绘制鼠标图层,消除了鼠标因为自身不停的被覆盖再绘制所产生的闪烁。

二、程序设计创新点

1、 描述创新点1,关键代码及结果截图

  • 创新点1

设置桌面图标,点击桌面图标,打开窗口,图标颜色变深,关闭窗口,图标颜色变浅,最小化窗口时,图标颜色保持不变。

实现思路:点击桌面图标时,必须满足***两个要素***,鼠标在图标的范围内,鼠标检测到左键单击。满足这两个条件后,按照如下步骤编写程序:首先判断当前是否窗口显示,如果没有窗口显示,说明还没有打开窗口,使用sheet_slide函数移动小窗口到指定打开位置,将其高度设置为1,并初始化图标,这里选用1进行初始化,1表示将图标颜色加深,最后刷新图标;如果当前是小窗口/大窗口显示,点击图标会将窗口隐藏(参考windows)。

  • 关键代码

在这里插入图片描述
细节说明:这里特别对图标打开和关闭的图标颜色进行了设置,关键实现在于Init_tubiao中参数flag,下面从代码角度进行说明。
在这里插入图片描述

  • 结果截图
    在这里插入图片描述
    在这里插入图片描述

、 描述创新点,关键代码及结果截图

  • 创新点

实现窗口的最大化,最小化和关闭功能

实现思路: 窗口的最大化,最小化以及关闭实现的关键是判断当前桌面的情况,我在设计的时候分为3种情况,一是小窗口在显示,二是大窗口在显示,三是没有窗口在显示,并根据其状态设置状态标志位。根据状态标志位对最大化,最小化和关闭进行分类处理。以上处理均是在按下鼠标左键的前提下进行的。

  • 关键代码

对状态标志位的设置
在这里插入图片描述
当鼠标移动到关闭按钮的区域时:对于关闭的情况,无论此时是大窗口还是小窗口都需要将窗口隐藏,所以调用sheet_updown函数将高度设置为-1。接下来考虑图标颜色的处理,关闭窗口后,图标颜色应该变浅,所以需要调用init_tubiao 函数将flag设为0传入,设置图标颜色后再刷新。代码实现如下图:
在这里插入图片描述
当鼠标移动到最大化按钮的区域时:对于最大化的情况,如果当前窗口是大窗口,按下最大化后,窗口会还原成小窗口,如果是小窗口,就是将窗口最大化。我在实现的时候使用了两种图层,一个小窗口的图层和一个大窗口的图层,这样最大化最小化切换的时候,只需要设置图层高度即可。代码实现如下:
在这里插入图片描述
当鼠标移动到最小化按钮的区域时:对于最小化的情况,无论当前是大窗口还是小窗口,最小化后均需将窗口隐藏。实现代码如下:
在这里插入图片描述
细节说明:注意到windows系统下,最大化前后图标是有区别的,这里仿照windows系统对最大化图标按下前后进行了改变的设置。代码实现如下:
在这里插入图片描述

  • 结果截图

最大化还原
在这里插入图片描述在这里插入图片描述
最小化
在这里插入图片描述在这里插入图片描述
关闭
在这里插入图片描述在这里插入图片描述

3 、 描述创新点3,关键代码及结果截图

  • 创新点3

实现窗口的移动
实现思路:当按下鼠标左键时,且鼠标在窗口的上方位置,判定为可以移动窗口。需要注意的是对窗口移动的位置进行判断,要保证移到显示界面外后还能将窗口拖回来(这里仍在存在一个细小的bug,移动到下方无法拖回,原因是对边界的检测错误)

  • 关键代码

如果坐标超出表示范围,对坐标进行修正,确定好合法的移动后的坐标后,调用sheet_slide函数移动窗口即可。实现代码如下:
在这里插入图片描述

  • 结果截图
    在这里插入图片描述在这里插入图片描述

4 、 描述创新点4,关键代码及结果截图

  • 创新点4

仿照windows,当鼠标移动小窗口到边界时,如果鼠标碰到边界,小窗口就会最大化。
实现思路:对鼠标的坐标进行判断,如果鼠标的坐标移动到边界时,此时按下鼠标左键,则窗口就会最大化。代码层面是需要隐藏小窗口图层,设置大窗口图层高度即可。

  • 关键代码

在这里插入图片描述

  • 结果截图
    在这里插入图片描述在这里插入图片描述

5、 描述创新点5,关键代码及结果截图

  • 创新点5

仿照windows,当窗口最大化后,如果拖到窗口上方,可以将其最小化。
实现思路:对鼠标的坐标进行判断,如果鼠标的坐标移动到大窗口上方标题栏,此时按住鼠标左键拖动,窗口就会最小化。代码层面是需要隐藏大窗口图层,设置小窗口图层高度即可。

  • 关键代码

在这里插入图片描述

  • 结果截图
    在这里插入图片描述在这里插入图片描述

6、 描述创新点6,关键代码及结果截图

  • 创新点6

增加一个画笔功能
实现思路:采用直线的两点式方程,对x0,y0,x1,y1确定的直线进行绘制

  • 关键代码

在这里插入图片描述

  • 结果截图

实现的效果比较粗糙,还可以继续优化。
在这里插入图片描述

三、实验心得体会

本次实验是自制操作系统的第11天,本次实验涉及到的内容比较零碎,但都不难,而且十分有趣。harib08a,b两个部分实现了支持鼠标画面外显示的功能;harib08c从代码角度省略了shtctl,简化了一部分代码,但是同时也增加了图层结构体的大小;harib08d修改图层高度,做了一个小实验,高度低的图层会被高度高的图层遮盖;harib08f增加了计数器功能,没有接收鼠标或者按键信息时,不再执行hlt;harib08g,h对计数器显示存在闪烁问题进行了修改和优化。相对来说,比较难的是节点考核,难点在于在有限时间内尽可能完善每一个功能,考虑到各种可能的情况,比如这次选题2的窗口移动的边界处理。总体来说,基本实现节点考核的要求,并对其中的一些细节进行了完善,但还可以去做和实现的功能还有很多,后期可以继续实现,算是为以后的大设计做一些基础性的准备的。

猜你喜欢

转载自blog.csdn.net/weixin_44595362/article/details/113925907