basekernel/user/manager.c

217 lines
5.4 KiB
C

/*
Copyright (C) 2016-2019 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file LICENSE for details.
*/
/*
Simple window manager runs a list of programs and distributes
events to each based on which one currently has the focus.
*/
#include "library/syscalls.h"
#include "library/string.h"
#include "library/stdio.h"
#include "library/kernel_object_string.h"
#include "library/nwindow.h"
#include "library/errno.h"
#define NWINDOWS 4
#define WINDOW_TITLE_HEIGHT 14
#define WINDOW_TITLE_ACTIVE_COLOR 100,100,255
#define WINDOW_TITLE_INACTIVE_COLOR 25,25,50
#define WINDOW_TITLE_TEXT_COLOR 255,255,255
#define WINDOW_BORDER_COLOR 200,200,200
#define WINDOW_BORDER 3
#define WINDOW_TEXT_PADDING 3
#define CLOSE_BOX_PADDING 3
#define CLOSE_BOX_SIZE (WINDOW_TITLE_HEIGHT-CLOSE_BOX_PADDING*2)
#define CLOSE_BOX_COLOR 100,100,100
struct window {
int w,h,x,y;
int console_mode;
const char * exec;
const char * arg;
int argc;
int pid;
int fds[6];
};
struct nwindow *nw = 0;
struct window windows[NWINDOWS] = {
{ .x=0, .y=0, .console_mode=1, .exec = "bin/shell.exe", .arg=0, .argc = 2 },
{ .x=0, .y=0, .console_mode=0, .exec = "bin/saver.exe", .arg=0, .argc = 2 },
{ .x=0, .y=0, .console_mode=0, .exec = "bin/snake.exe", .arg=0, .argc = 2 },
{ .x=0, .y=0, .console_mode=1, .exec = "bin/fractal.exe", .arg=0, .argc = 2 },
};
void draw_border( struct window *win, int isactive )
{
int x=win->x;
int y=win->y;
int h=win->h;
int w=win->w;
// Title bar
if(isactive) {
nw_bgcolor(nw,WINDOW_TITLE_ACTIVE_COLOR);
} else {
nw_bgcolor(nw,WINDOW_TITLE_INACTIVE_COLOR);
}
nw_clear(nw,x,y,w,WINDOW_TITLE_HEIGHT);
// Close box
nw_fgcolor(nw,CLOSE_BOX_COLOR);
nw_rect(nw,x+CLOSE_BOX_PADDING,y+CLOSE_BOX_PADDING,CLOSE_BOX_SIZE,CLOSE_BOX_SIZE);
// Title text
nw_fgcolor(nw,WINDOW_TITLE_TEXT_COLOR);
nw_string(nw,x+CLOSE_BOX_SIZE+CLOSE_BOX_PADDING*2,y+WINDOW_TEXT_PADDING,win->exec);
// Border box
nw_fgcolor(nw,WINDOW_BORDER_COLOR);
nw_line(nw,x,y,w,0);
nw_line(nw,x,y+WINDOW_TITLE_HEIGHT-1,w,0);
nw_line(nw,x,y,0,h);
nw_line(nw,x+1,y,0,h);
nw_line(nw,x,y+h,w,0);
nw_line(nw,x+1,y+h,w,0);
nw_line(nw,x+w,y,0,h);
nw_line(nw,x+w+1,y,0,h);
nw_bgcolor(nw,0,0,0);
}
int main(int argc, char *argv[])
{
/* Obtain the default window from parent process. */
nw = nw_create_default();
nw_clear(nw,0, 0, nw_width(nw), nw_height(nw));
nw_flush(nw);
/* Distribute window locations across screen. */
int i;
for(i=0;i<NWINDOWS;i++) {
struct window *w = &windows[i];
w->x = i%2 ? nw_width(nw)/2 : 0;
w->y = i/2 ? nw_height(nw)/2 : 0;
w->w = nw_width(nw)/2-2;
w->h = nw_height(nw)/2-2;
//printf("window %d %d %d %d %d %s\n",i,w->w,w->h,w->x,w->y,w->exec);
}
/* Open each window and connect the various pipes. */
for(i=0;i<NWINDOWS;i++) {
struct window *w = &windows[i];
struct nwindow *child = nw_create_child(nw,w->x+WINDOW_BORDER, w->y+WINDOW_TITLE_HEIGHT, w->w-WINDOW_BORDER*2, w->h-WINDOW_BORDER-WINDOW_TITLE_HEIGHT);
int window_fd = nw_fd(child);
if(w->console_mode) {
w->fds[0] = syscall_open_console(window_fd);
w->fds[1] = w->fds[0];
w->fds[2] = w->fds[0];
w->fds[3] = window_fd; // doesn't need a window fd
w->fds[4] = 4;
w->fds[5] = 5;
} else {
w->fds[0] = -1; // doesn't need stdin/stdout
w->fds[1] = -1;
w->fds[2] = -1;
w->fds[3] = window_fd;
w->fds[4] = 4;
w->fds[5] = 5;
}
draw_border(w,0);
nw_bgcolor(child,0,0,0);
nw_flush(nw);
const char *args[3];
args[0] = w->exec;
args[1] = w->arg;
args[2] = 0;
int pfd = syscall_open_file(KNO_STDDIR,w->exec,0,0);
if(pfd>=0) {
w->pid = syscall_process_wrun(pfd, w->argc, args, w->fds, 6);
if(w->pid<0) {
nw_string(child,10,10,"Unable to start process:");
nw_string(child,10,20,w->exec);
nw_string(child,10,30,strerror(pfd));
nw_flush(child);
/* keep going, let other processes run. */
}
} else {
nw_string(child,10,10,"Unable to access program:");
nw_string(child,10,20,w->exec);
nw_string(child,10,30,strerror(pfd));
nw_flush(child);
/* keep going, let other processes run. */
}
}
/* Finally, allow the user to switch between windows*/
int active = 0;
/* Draw green window around active process */
draw_border(&windows[active],1);
nw_flush(nw);
/* Now wait for events to arrive at the manager. */
struct event e;
while (nw_next_event(nw,&e)) {
if(e.type==EVENT_CLOSE) break;
if(e.type!=EVENT_KEY_DOWN) continue;
char c = e.code;
if (c == '\t') {
/* If tab entered, go to the next process */
/* Draw white boundary around old window. */
draw_border(&windows[active],0);
nw_flush(nw);
active = (active + 1) % NWINDOWS;
/* Draw green window around new window. */
draw_border(&windows[active],1);
nw_flush(nw);
} else if (c=='~') {
/* If tilde entered, cancel the whole thing. */
break;
} else {
if(windows[active].console_mode) {
// Post a single character to the console.
syscall_object_write(windows[active].fds[KNO_STDIN],&c,1,KERNEL_IO_POST);
} else {
// Post a complete event to the window.
syscall_object_write(windows[active].fds[KNO_STDWIN],&e,sizeof(e),KERNEL_IO_POST);
}
}
}
/* Reap all children processes */
for (i=0;i<NWINDOWS;i++) {
syscall_process_reap(windows[i].pid);
}
/* XXX should kill child process here */
/* Clean up the window */
nw_clear(nw, 0, 0, nw_width(nw), nw_height(nw));
nw_flush(nw);
return 0;
}