• 自制操作系统日志——第二十四天


    自制操作系统日志——第二十四天

    今天呢,我们主要进行窗口的操作,诸如进行窗口切换,移动窗口,用鼠标切换窗口等等,下面让我们正式开始吧!



    一、窗口切换

    我们先来个简单的窗口切换,练练手吧!按下F11,将最底层的图层放置最上层。
    F11 ⇒ 0x57

    bootpack.c:

    			   if(i == 256 + 0x57 && shtctl->top > 2){//F11
    					sheet_updown(shtctl->sheets[1], shtctl->top -1); //将除背景外的最底层放到鼠标图层的下一层
    				   }
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    这确实没啥难度了。

    下面,让我们使用鼠标点击图层进行显示。当鼠标进行点击时候,我们需要按照从上到下的顺序进行判断,鼠标落在哪一个图层的范围里:

    bootpack.c:主函数:

    int j, x, y;
    	struct SHEET *sht;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;
    								}
    							}
    						}
    					}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    喔喔,成功了嘿嘿嘿!

    二、移动窗口

    其实,我们之前已经实现过一部分的窗口移动了。那么接下来,我们就在之前的基础上改造一下咯:

    当鼠标点击标题栏时候,就进入到窗口移动模式,当鼠标松开左键后,就推出窗口的移动模式,返回通常模式。 除此之外,还需要新增两个变量,mmx、mmy,用于记录移动之前的坐标。并结合mx、my来计算鼠标移动的距离。

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    然后运行:
    在这里插入图片描述

    看看,真的成功了,嘿嘿嘿!!!其实仔细看看这一段代码也不是很难的,是吧? 是的!!!

    三、用鼠标关闭窗口

    emm,其实这一部分也不难,首先就是鼠标移到x这个地方的判断和上述几个是一样的。至于关闭程序呢,就和前面的强制结束程序代码是一样的:

    										if(sht->bxsize -21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19){
    											//点击X按钮
    											if(sht->task != 0){
    												cons = (struct CONSOLE *) *((int *) 0xfec);
    												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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    试试看:
    在这里插入图片描述
    在这里插入图片描述
    点击一下,确实是关闭了,嘿嘿嘿!!不过目前我们只做了关闭程序的命令,至于命令行和其他图层窗口的关闭后续还会接着讲喔。

    四、将输入切换到应用程序窗口

    我们先制作利用tab进行切换。当我们按下tab时候,键盘输入切换到当前输入窗口的下一次窗口之中,若该窗口是最下层则切换到最上层。
    首先,我们设置启用自动关闭窗口的功能,为了区分,我们将应用程序的flags值与0x10进行OR运算,使其成为0x1[] :
    console.c:
    hrb_api

    else if (edx == 5){
    		sht = sheet_alloc(shtctl);
    		sht->task = task;
    		sht->flags |= 0x10;//启动应用程序自动关闭窗口的功能
    
    • 1
    • 2
    • 3
    • 4

    cmd_app

    			for(i = 0; i <MAX_SHEETS; i++){
    				sht = &(shtctl->sheets0[i]);
    				if((sht->flags & 0x11) == 0x11 && sht->task == task)
    				{
    					//找到被应用程序遗留的窗口
    					sheet_free(sht);//关闭
    				}
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后,我们需要制作一个改变窗口标题栏颜色的函数:
    window.c

    //改变标题栏的颜色
    void change_wtitle8(struct SHEET *sht, char act)
    {
    	int x, y, xsize = sht->bxsize;
    	char c, tc_new, tbc_new, tc_old, tbc_old, *buf = sht->buf;
    	if (act != 0) {
    		tc_new  = COL8_FFFFFF;
    		tbc_new = COL8_000084;
    		tc_old  = COL8_C6C6C6;
    		tbc_old = COL8_848484;
    	} else {
    		tc_new  = COL8_C6C6C6;
    		tbc_new = COL8_848484;
    		tc_old  = COL8_FFFFFF;
    		tbc_old = COL8_000084;
    	}
    	for (y = 3; y <= 20; y++) {
    		for (x = 3; x <= xsize - 4; x++) {
    			c = buf[y * xsize + x];
    			if (c == tc_old && x <= xsize - 22) {
    				c = tc_new;
    			} else if (c == tbc_old) {
    				c = tbc_new;
    			}
    			buf[y * xsize + x] = c;
    		}
    	}
    	sheet_refresh(sht, 3, 3, xsize, 21);
    	return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    然后修改一下主函数里的内容:
    bootpack.c:

    	int key_shift = 0, key_leds = (binfo->leds >> 4) & 7, keycmd_wait = -1;
    	int j, x, y, mmx = -1, mmy = -1;//mm 记录鼠标移动之前的坐标;由于鼠标指针不会移到画面外,因此设置-1表不移动状态
    	struct SHEET *sht = 0, *key_win;
    	略
    	key_win = sht_win;//用key_win存放当前窗口的地址
    	sht_cons->task = task_cons;
    	sht_cons->flags |= 0x20; //有光标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_C6C6C6, s, 1);
    							cursor_x += 8;
    						}
    					}else{
    						//发送给命令行
    						fifo32_put(&task_cons->fifo, s[0] + 256);
    					}
    				}
    				if(i == 256 + 0x0e ) //退格键
    				{//用空格把光标消去后移动一次
    					if(key_win == sht_win){
    						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);//z这里定义在console中退格键的编码为8
    					}
    				}
    				if(i == 256 + 0x1c){//Enter
    					if(key_win != sht_win){//发送到命令行窗口
    						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);
    				}					
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    最后,在添加一下控制窗口标题栏颜色和task_a光标的函数:

    //控制窗口标题栏的颜色和task_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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    用key_win变量存放当前处于输入模式的窗口地址。

    在这里插入图片描述
    那么,进一步的我们使用鼠标进行切换输入窗口吧!

    	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);
    										}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    改变一点点即可:
    在这里插入图片描述

    定时器API

    接下来,让我们再制作一个定时器的API:
    获取定时器(alloc):

    • EDX = 16
    • EAX = 定时器句柄(由操作系统进行返回的)

    设置定时器发送的数据(init):

    • EDX = 17
    • EBX = 定时器句柄
    • EAX = 数据

    定时器设定(set):

    • EDX = 18
    • EBX = 定时器句柄
    • EAX = 时间

    释放定时器(free):

    • EDX = 19
    • EBX = 定时器句柄

    console.c:

    else if (edx == 15){if( i >= 256 ){//键盘数据,通过A,接收256以上的数据,即除了键盘数据外还接受定时器发送的数据
    				reg[7] = i - 256;
    				return 0;
    			}
    		}
    	}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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    再写一个程序,用于显示时间的,这个程序的定时器超时时会发送128的值,传递到缓冲区时会+256的。 当我们的数据不是128时,证明系统收到了其他的数据:
    noodle.c

    #include 
    
    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();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    运行看看:
    在这里插入图片描述

    嚯嚯,成功!

    emm 等等好像有bug,当我们点击关闭程序时候,大概1s过后,命令行会接受到如下数据:
    在这里插入图片描述

    这是因为,我们关闭程序后,其定时器并未立刻取消,也就是说它在1s后仍然会发送数据给缓冲区,这样子命令行就会接收到这个数据。此时命令行就会搞得很懵了!!因此,我们需要改进一下:我们要把待机中的定时器给取消掉!
    timer.c:

    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_timer;
    
               timerctl.t0 = t;
               timerctl.next_time = t->timeout;
            }else{
                //非第一个定时器的处理,找到timer前一个定时器
                t = timerctl.t0;
                for(;;){
                    if(t->next_timer == timer){
                        break;
                    }
                    t = t->next_timer;
                }
                t->next_timer = timer->next_timer;//将timer的下一个指向timer的下一个
            }
            timer->flags = TIMER_FLAGS_ALLOC;
            io_store_eflags(e);
            return 1;//处理成功
        }
        io_store_eflags(e);
        return 0;//不需要取消处理
    }
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    这里还要设置一个flags2用于启用自动取消功能:

    struct TIMER
    {
    	struct TIMER *next_timer;//指下一个定时器的地址
    	unsigned int timeout;// timeout的含义,这里指予定时刻,通过settime中赋予的超时时间+当前时刻来计算,当到达多少时间后算超时
    	char flags, flags2;//flags表示各个定时器的状态;flags2 代表是否开启自动取消
    	struct FIFO32 *fifo;//用于将超时的信息传给缓冲区。
    	int data;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后,相对应的修改一下:
    timer.c:

    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;//没找到
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    console.c:

    else if (edx == 16) {
    		reg[7] = (int) timer_alloc();
    		((struct TIMER *) reg[7])->flags2 = 1;//运行自动取消
    	}
    
    • 1
    • 2
    • 3
    • 4

    下面就继续编写一个函数来进行取消:
    cmd_app:

    			for(i = 0; i <MAX_SHEETS; i++){
    				sht = &(shtctl->sheets0[i]);
    				if((sht->flags & 0x11) == 0x11 && sht->task == task)
    				{
    					//找到被应用程序遗留的窗口
    					sheet_free(sht);//关闭
    				}
    			}
    			timer_cancelall(&task->fifo);
    			memman_free_4k(memman, (int) q, segsiz);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后运行看看:

    在这里插入图片描述

    嘿嘿,成功!


    总结

    好了,今天到此为止了。有点劳累,好好休息,明天继续~😜

  • 相关阅读:
    Python数学建模-2.6文件操作
    kafka部分partition的leader=-1修复方案整理
    linux bash forkBomb 炸弹测试脚本程序
    一行代码安装Tensorflow和Keras以及pytorch环境
    呼叫中心系统信息发送功能的应用
    【Spring Boot】日志文件
    用Python实现链表---单向循环链表
    技术保证质量,软件测试的这些测试方法你都掌握了吗?
    Opencv项目实战:03 扫描二维码&条形码
    Beyond Homophily: Structure-aware Path Aggregation Graph Neural Network
  • 原文地址:https://blog.csdn.net/qq_43696276/article/details/126232830