Skip to content

Commit 6cc00e7

Browse files
committed
Add desktop environment
1 parent 58e9431 commit 6cc00e7

File tree

6 files changed

+271
-13
lines changed

6 files changed

+271
-13
lines changed

Makefile

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# Program to auto start when kernel is ready.
22
# List of program can be found at build/usr/bin/
33
# Some of them are
4-
# sh.out
5-
# ls.out
6-
# tictactoe.out
7-
# calc.out
8-
INIT_APPNAME ?= sh
4+
# sh
5+
# ls
6+
# tictactoe
7+
# calc
8+
# desktop
9+
INIT_APPNAME ?= desktop
910

1011
ROOT_DIR = .
1112
BUILD_DIR = build

src/usr/include/process.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ namespace std {
2222
// if non-negative value is returned
2323
// then it's the pid of the new process
2424
// otherwise it's some error code
25-
int spawnl(char *path, char *arg0, ...);
26-
int spawnv(char *path, char *argv[]);
25+
int spawnl(const char *path, const char *arg0, ...);
26+
int spawnv(const char *path, const char *argv[]);
2727

2828
int getpid();
2929

src/usr/lib/process.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
#include <stddef.h>
44
#include <sys/syscall.h>
55

6-
int spawnl(char *file_path, char *arg0, ...) {
6+
int spawnl(const char *file_path, const char *arg0, ...) {
77
va_list args;
88
va_start(args, arg0);
9-
char *argv[PROCESS_MAX_ARGC] = {NULL};
9+
const char *argv[PROCESS_MAX_ARGC] = {NULL};
1010

1111
// prepare argv
1212
{
@@ -26,9 +26,9 @@ int spawnl(char *file_path, char *arg0, ...) {
2626
return rv;
2727
}
2828

29-
int spawnv(char *file_path, char *argv[]) {
29+
int spawnv(const char *file_path, const char *argv[]) {
3030
// kernel expects argv size must be PROCESS_MAX_ARGC
31-
char *argv_resized[PROCESS_MAX_ARGC] = {NULL};
31+
const char *argv_resized[PROCESS_MAX_ARGC] = {NULL};
3232
for (int i = 0; i < PROCESS_MAX_ARGC - 1; i++) {
3333
if (argv[i] == NULL)
3434
break;
@@ -38,7 +38,7 @@ int spawnv(char *file_path, char *argv[]) {
3838
return _spawnv_syscall(file_path, argv_resized);
3939
}
4040

41-
int _spawnv_syscall(char *file_path, char *argv[]) {
41+
int _spawnv_syscall(const char *file_path, const char *argv[]) {
4242
// kernel expects argv size must be PROCESS_MAX_ARGC
4343
int pid = SYSCALL_A3(SYSCALL_PROCESS, SYSCALL_PROCESS_SUB_SPAWN_FNAME,
4444
file_path, argv);

src/usr/local/src/desktop.cpp

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
// desktop environment
2+
#include <conio.h>
3+
#include <dirent.h>
4+
#include <graphics.h>
5+
#include <iostream.h>
6+
#include <process.h>
7+
#include <utility.h>
8+
#include <vector.h>
9+
10+
const int WINDOW_HEIGHT = GRAPHICS_MAX_HEIGHT;
11+
const int WINDOW_WIDTH = GRAPHICS_MAX_WIDTH;
12+
13+
enum IconType { DATA_FILE = 0, EXECUTABLE = 1 };
14+
15+
struct IconData {
16+
std::string fname;
17+
IconType type;
18+
};
19+
20+
class GraphicsModeController {
21+
int gd, gm;
22+
bool is_graphics_enabled;
23+
24+
public:
25+
GraphicsModeController() : is_graphics_enabled(false) {
26+
// assume to be in text-mode at start
27+
}
28+
29+
void graphics_mode_enable() {
30+
if (is_graphics_enabled)
31+
return;
32+
gd = DETECT;
33+
34+
std::graphics::initgraph(&gd, &gm, NULL);
35+
int gerr = std::graphics::graphresult();
36+
if (gerr != 0) {
37+
std::cout << "failed to open graphics mode, err: " << gerr
38+
<< std::endl;
39+
std::exit(1);
40+
}
41+
is_graphics_enabled = true;
42+
}
43+
44+
void graphics_mode_disable() {
45+
if (!is_graphics_enabled)
46+
return;
47+
48+
std::graphics::closegraph();
49+
is_graphics_enabled = false;
50+
}
51+
} gmcontroller;
52+
53+
class Desktop {
54+
const int ICON_WIDTH = 160;
55+
const int ICON_COUNT_HOR = WINDOW_WIDTH / ICON_WIDTH;
56+
57+
int width, height;
58+
std::vector<IconData> files;
59+
60+
int text_height;
61+
int text_width;
62+
int cursor_id;
63+
64+
int spawn(std::vector<std::string> &argv) {
65+
// argv[0] == executable filename
66+
const char **argvp = new const char *[argv.size() + 1];
67+
for (int i = 0; i < argv.size(); i++) {
68+
argvp[i] = argv[i].c_str();
69+
}
70+
argvp[argv.size()] = NULL;
71+
const char *filename = argvp[0];
72+
int pid = std::spawnv(filename, argvp);
73+
if (pid < 0) {
74+
// failed
75+
return pid;
76+
}
77+
int last_status_code;
78+
std::waitpid(pid, &last_status_code);
79+
delete[] argvp;
80+
return last_status_code;
81+
}
82+
83+
public:
84+
Desktop(int width, int height) : width(width), height(height) {
85+
text_width = std::graphics::textwidth(" ");
86+
text_height = std::graphics::textheight(" ");
87+
88+
cursor_id = 0;
89+
gmcontroller.graphics_mode_enable();
90+
}
91+
92+
void refresh() {
93+
struct std::DIR dir;
94+
std::opendir(&dir);
95+
struct std::dirent *dp;
96+
97+
files.clear();
98+
while ((dp = std::readdir(&dir)) != NULL) {
99+
bool executable = dp->flag & std::DIRENT_EXECUTABLE;
100+
IconData file{
101+
.fname = std::string(dp->d_name),
102+
.type = executable ? EXECUTABLE : DATA_FILE,
103+
};
104+
files.push_back(file);
105+
}
106+
draw();
107+
}
108+
109+
void launch_app(int app_id) {
110+
// switch to text-mode
111+
gmcontroller.graphics_mode_disable();
112+
113+
std::vector<std::string> argv;
114+
if (files[app_id].type == EXECUTABLE) {
115+
argv.push_back(files[app_id].fname);
116+
} else {
117+
// execute: more <filename>
118+
argv.push_back(std::string("more"));
119+
argv.push_back(files[app_id].fname);
120+
}
121+
122+
std::clrscr();
123+
int rv = spawn(argv);
124+
125+
std::cout << "exited: " << rv << std::endl;
126+
std::cout << "Press any key to return to desktop." << std::endl;
127+
std::getch();
128+
129+
// // switch back to graphics mode
130+
gmcontroller.graphics_mode_enable();
131+
}
132+
133+
void controller(char c) {
134+
// assumes at least one file.
135+
int id_count = files.size();
136+
137+
switch (c) {
138+
case 'd':
139+
cursor_id = (cursor_id + 1) % id_count;
140+
break;
141+
case 'a':
142+
cursor_id = (cursor_id + id_count - 1) % id_count;
143+
break;
144+
case 'w':
145+
cursor_id = (cursor_id - ICON_COUNT_HOR + id_count) % id_count;
146+
break;
147+
case 's':
148+
cursor_id = (cursor_id + ICON_COUNT_HOR) % id_count;
149+
break;
150+
case '\n':
151+
// selected
152+
launch_app(cursor_id);
153+
break;
154+
}
155+
}
156+
157+
void execute() {
158+
do {
159+
refresh();
160+
controller(std::getch());
161+
} while (1);
162+
}
163+
164+
void draw_icon(int id, const IconData &file, int y_offset) {
165+
int x = (id % ICON_COUNT_HOR) * ICON_WIDTH;
166+
int y = (id / ICON_COUNT_HOR) * text_height + y_offset;
167+
168+
const int half_border = 1;
169+
170+
const int max_length = (ICON_WIDTH - half_border * 2) / text_width;
171+
std::string fname = file.fname;
172+
if (fname.length() > max_length) {
173+
fname[max_length] = '\0';
174+
}
175+
176+
// highlight selected icon
177+
if (id == cursor_id) {
178+
std::graphics::setcolor(YELLOW);
179+
} else {
180+
std::graphics::setcolor(WHITE);
181+
}
182+
std::graphics::bar(x, y, x + ICON_WIDTH - 1, y + text_height - 1);
183+
184+
// draw icon and text
185+
if (file.type == EXECUTABLE) {
186+
std::graphics::setcolor(GREEN);
187+
} else {
188+
// data file
189+
std::graphics::setcolor(BLACK);
190+
}
191+
std::graphics::outtextxy(x, y, fname.c_str());
192+
}
193+
194+
void draw() {
195+
const int banner_border = 2;
196+
197+
// draw banner
198+
{
199+
std::graphics::setcolor(LIGHT_BLUE);
200+
std::graphics::bar(0, 0, this->width - 1,
201+
text_height + banner_border * 2 - 1);
202+
int x_offset = banner_border;
203+
std::graphics::setcolor(YELLOW);
204+
x_offset += std::graphics::outtextxy(x_offset, banner_border,
205+
"| FuzzyOS | ");
206+
std::graphics::setcolor(BLACK);
207+
x_offset += std::graphics::outtextxy(x_offset, banner_border,
208+
"Control: w,a,s,d,enter");
209+
}
210+
211+
// draw icons
212+
{
213+
std::graphics::setcolor(LIGHT_GRAY);
214+
std::graphics::bar(0, text_height + banner_border * 2,
215+
this->width - 1, this->height - 1);
216+
217+
for (int i = 0; i < files.size(); i++) {
218+
draw_icon(i, files[i], text_height + banner_border * 2);
219+
}
220+
}
221+
std::graphics::graphflush();
222+
}
223+
};
224+
225+
void start_dm() {
226+
std::graphics::graphautoflush_disable();
227+
std::graphics::setbkcolor(BLACK);
228+
229+
Desktop d(GRAPHICS_MAX_WIDTH, GRAPHICS_MAX_HEIGHT);
230+
d.execute();
231+
}
232+
233+
void cleanup_graphics() {
234+
gmcontroller.graphics_mode_disable();
235+
std::cout << "heap memory usage at exit "
236+
<< (std::benchmark_get_heap_usage()) << " bytes" << std::endl;
237+
std::cout << "heap memory area at exit " << (std::benchmark_get_heap_area())
238+
<< " bytes" << std::endl;
239+
}
240+
241+
int show_usage() {
242+
std::cout << "desktop environment" << std::endl;
243+
return 0;
244+
}
245+
246+
int main(int argc, char *argv[]) {
247+
if (argc != 1) {
248+
// assumes user wants desktop --help
249+
return show_usage();
250+
}
251+
252+
int gd = DETECT, gm;
253+
// graphics started
254+
std::atexit(cleanup_graphics);
255+
start_dm();
256+
return 0;
257+
}

tests/fuzzy_shell_test.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ TEST_MAGIC_WANT="${MAGIC_WORD_SLEEP:?}"
77

88
os_test_up "${TEST_MAGIC_WANT:?}" "${TEST_INJECT_WORD:?}" || exit -1
99

10-
# Assumes Fuzzy OS starts with Shell
1110
source tests/app/shell_test.sh
1211

1312
python -m tests.qemu.monitor --quit

tests/lib.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ function os_test_up() {
151151
# Turn up QEMU in background
152152
make clean BUILD_DIR="${BUILD_TEST_DIR:?}" \
153153
&& make qemu \
154+
INIT_APPNAME=sh \ # start OS with terminal
154155
SRC_DIR="${SRC_TEST_DIR:?}" \
155156
BUILD_DIR="${BUILD_TEST_DIR:?}" \
156157
QEMU_SHUT_FLAGS="" \

0 commit comments

Comments
 (0)