diff --git a/README.md b/README.md
index 4cba96f..66879cb 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,9 @@
## Overview
-Pygame GUI library, named after the creator of Python, Guido van Rossum, believing that this library will be the first to implement a GUI completely compatible with pygame. Included is a "mini-OS" to show off all the elements.
+Pygame GUI library, named after the creator of Python, Guido van Rossum, believing that this library will be the first to implement a GUI completely compatible with pygame. Included is a "mini-OS" to show off all the elements. That "mini-OS" is called *Winnux 58* (**Win**dows 9**5** + **Win**dows 9**8** + Li**nux**).
+
+~~i kinda hate this name now~~
## Features
@@ -20,9 +22,32 @@ Please check our [wiki](https://github.com/The-UltimateGamer/GUIdo/wiki) for mor
## NEVER TRY TO MOUNT `files.img`
## Directory structure:
-/home/
-- test.txt
-(Note: this is created for the testing of the `cat` command.)
-/sys/
-- kernel.py
-(This is where our OS boots.)
+- `/home/`
+
+ - test.txt
+ (Note: this is created for the testing of the `cat` command.)
+
+- `/sys/`
+
+ - kernel.py
+ (This is where our OS boots. **DO NOT TRY TO DELETE IT.**)
+
+## License Notice
+
+Winnux 58 is brought to you under the [GNU AGPL-3.0 License](https://www.gnu.org/licenses/agpl-3.0.en.html).
+
+OS emulator using pygame.
+Copyright (C) 2020 The-UltimateGamer & pythonleo
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
\ No newline at end of file
diff --git a/README.pdf b/README.pdf
new file mode 100644
index 0000000..2b72003
Binary files /dev/null and b/README.pdf differ
diff --git a/files.img b/files.img
index eafb9f1..4696092 100644
--- a/files.img
+++ b/files.img
@@ -575,3 +575,12 @@ If you're seeing this, then the `cat` command is working!
!LOC=/
!FNAME=home/
+
+
+
+!LOC=/
+!FNAME=a.txt
+
+
+
+
diff --git a/kernel.py b/kernel.py
new file mode 100644
index 0000000..af62be5
--- /dev/null
+++ b/kernel.py
@@ -0,0 +1,563 @@
+# kernel.py
+# Core of our OS
+
+import pygame, sys, random, time, io, os
+from math import *
+from contextlib import redirect_stdout
+from enum import Enum
+
+width, height = 1024, 768
+
+def pwd(working_dir, args):
+ print(working_dir, end='\r')
+
+def ls(working_dir, args, flag=True):
+ files = open("files.img", 'r+', encoding="ISO-8859-1")
+ lines = files.read().strip('\0').split('\n')
+
+ result = []
+ for i, line in enumerate(lines):
+ if line == "!LOC=%s" % working_dir:
+ if flag: print(lines[i + 1].strip('!FNAME='), end='\r')
+ result.append(lines[i + 1].strip('!FNAME='))
+ return result
+
+def cat(working_dir, args):
+ if not args:
+ print("Missing argument.\rUsage: cat ", end='\r')
+ return
+ for target in args:
+ files = open("files.img", 'r+', encoding="ISO-8859-1")
+ lines = files.read().strip('\0').split('\n')
+ contents = []
+ for i, line in enumerate(lines):
+ if line == '!LOC=%s' % working_dir \
+ and lines[i + 1] == '!FNAME=%s' % target:
+ for i in range(i+2, len(lines)):
+ if lines[i] and lines[i][0] == '!': break
+ contents.append(lines[i])
+ break
+ else:
+ print("cat: %s: no such file" % target, end = '\r')
+ print('\r'.join(contents))
+ files.close()
+
+def rm(working_dir, args):
+ files = open("files.img", 'r+', encoding="ISO-8859-1")
+ to_be_written = []
+ target = args[0]
+ lines = files.read().strip('\0').split('\n')
+ write_or_not = True
+ for i, line in enumerate(lines):
+ if write_or_not:
+ if line == '!FNAME=%s' % target \
+ and lines[i-1] == '!LOC=%s' % working_dir:
+ write_or_not = False
+ to_be_written.pop()
+ else:
+ if line and line[0] == '!':
+ write_or_not = True
+ if write_or_not:
+ to_be_written.append(line)
+ files.close()
+ fw = open("files.img", 'w+', encoding="ISO-8859-1")
+ fw.write('\n'.join(to_be_written))
+ fw.close()
+
+class Pic(object):
+ def __init__(self, fileName):
+ img = pygame.image.load(fileName)
+ self.img = pygame.transform.scale(img, (width, height))
+ self.x, self.y = 0, 0
+ self.w, self.h = self.img.get_size()
+ def draw(self, screen, speed = 5):
+ screen.blit(self.img, (self.x, self.y), (0, 0, self.w, self.h))
+ pygame.draw.rect(screen, (0, 255, 0), (0, 0, speed * 8, 8), 0)
+ def getPixelGrid(self, x0, y0, sideLen, pixelGrid):
+ n = sideLen // 2
+ for y in range(-n, n + 1):
+ for x in range(-n, n + 1):
+ pixelGrid[y + n][x + n] = self.img.get_at((x + x0, y + y0))
+
+class Kernel:
+ def __init__(self):
+ pygame.init()
+ self.screen = pygame.display.set_mode((width, height))
+ pygame.display.set_caption("Winnux 58")
+ self.clock = pygame.time.Clock()
+ self.raster = pygame.font.Font("res/Perfect_DOS_VGA_437.ttf", 15)
+ self.speed = 5
+ self.mousePos = (0, 0)
+ self.apps = []
+ self.appID = 0
+ self.dialogs = []
+ self.dialogID = 0
+ def launch(self):
+ for app in self.apps:
+ app.draw(self.screen)
+ for dialog in self.dialogs:
+ try:
+ dialog.draw(self.screen)
+ except:
+ pass
+ pygame.display.update()
+ self.clock.tick(50)
+ def addApp(self, app):
+ app.appID = len(self.apps)
+ self.apps.append(app)
+ def addDialog(self, dialog):
+ dialog.dialogID = len(self.dialogs)
+ dialog.closeBtn = Secret((dialog.x + 357, dialog.y + 6, 17, 16), dialog.dialogID)
+ self.dialogs.append(dialog)
+ def keyUp(self, key):
+ self.apps[self.appID].keyUp(key)
+ def keyDown(self, key):
+ self.apps[self.appID].keyDown(key)
+ def mouseDown(self, pos, button):
+ self.apps[self.appID].mouseDown(pos, button)
+ try:
+ self.dialogs[self.dialogID].mouseDown(pos, button)
+ except:
+ pass
+ print(event.pos)
+ def mouseUp(self, pos, button):
+ self.apps[self.appID].mouseUp(pos, button)
+ def mouseMotion(self, pos):
+ self.apps[self.appID].mouseMotion(pos)
+
+class App:
+ def __init__(self, picName):
+ self.pic = Pic(picName)
+ self.appID = 0
+ self.btnList = []
+ self.tooltipList = []
+ self.txtField = TxtField(0, 0, 0, 0)
+ self.txtFieldEnabled = False
+ self.canvas = pygame.Surface((width, height - 60))
+ self.canvasEnabled = False
+ self.secretList = []
+ def draw(self, screen):
+ if framework.appID != self.appID:
+ return
+ screen.blit(self.pic.img, (0, 0))
+ for button in self.btnList:
+ button.draw(screen)
+ for tooltip in self.tooltipList:
+ tooltip.draw(screen)
+ if self.txtFieldEnabled:
+ self.txtField.content = self.txtField.wrap(self.txtField.txtBuffer)
+ self.txtField.content = self.txtField.content[-self.txtField.h:]
+ self.txtField.draw(screen, self.txtField.content, (255, 255, 255), self.txtField.y)
+ if self.canvasEnabled:
+ if self.appID == snake.appID:
+ snakeGame.draw(self.canvas)
+ snakeGame.move()
+ framework.screen.blit(self.canvas, (0, 60))
+ def addButton(self, b):
+ self.btnList.append(b)
+ def addTooltip(self, txt, font, x, y, c, rect):
+ tt = Tooltip(txt, font, x, y, c, rect)
+ self.txtList.append(tt)
+ def enableTxtField(self, x, y, w, h):
+ if self.canvasEnabled:
+ print("Only one of either the text field or the canvas can be enabled in an App.")
+ return
+ self.txtFieldEnabled = True
+ self.txtField.x, self.txtField.y = x, y
+ self.txtField.w, self.txtField.h = w, h
+ def enableCanvas(self):
+ if self.txtFieldEnabled:
+ print("Only one of either the text field or the canvas can be enabled in an App.")
+ return
+ self.canvasEnabled = True
+ def mouseDown(self, pos, button):
+ for btn in self.btnList:
+ btn.mouseDown(pos, button)
+ for secret in self.secretList:
+ secret.mouseDown(pos, button)
+ def mouseUp(self, pos, button):
+ for button in self.btnList:
+ button.mouseUp(pos, button)
+ def mouseMotion(self, pos):
+ framework.mousePos = pos
+ for btn in self.btnList:
+ btn.mouseMove(pos)
+ def keyUp(self, key):
+ if self.txtFieldEnabled:
+ self.txtField.keyUp(key)
+ def keyDown(self, key):
+ if self.txtFieldEnabled:
+ self.txtField.keyDown(key)
+ if self.canvasEnabled:
+ if self.appID == snake.appID:
+ snakeGame.keyDown(key)
+
+class Button:
+ def __init__(self, picFile, x, y, appID, **txt):
+ self.img = pygame.image.load(picFile).convert()
+ self.w, self.h = self.img.get_width() // 3, self.img.get_height()
+ self.x, self.y = x, y
+ self.rect = pygame.Rect(self.x, self.y, self.w, self.h)
+ self.status = 0
+ self.appID = appID
+ self.txt = txt
+ def draw(self, screen):
+ screen.blit(self.img, (self.x, self.y),
+ (self.status * self.rect.w, 0,
+ self.rect.w, self.rect.h))
+ if self.txt:
+ screen.blit(self.txt["font"].render(self.txt["content"], True, (0,0,0)), \
+ (self.x + self.w // 2 - 4 * len(self.txt["content"]), self.y + self.h // 2 - 8))
+ def mouseDown(self, pos, button):
+ if self.rect.collidepoint(pos):
+ self.status = 2
+ def mouseUp(self, pos, button):
+ self.status = 0
+ if not self.rect.collidepoint(pos):
+ return
+ framework.apps[self.appID].pic.draw(framework.screen, framework.speed)
+ framework.appID = self.appID
+ def mouseMove(self, pos):
+ if self.rect.collidepoint(pos):
+ self.status = 1
+ else:
+ self.status = 0
+
+class Tooltip:
+ def __init__(self, txt, font, x, y, c, rect):
+ self.txt = txt
+ self.img = font.render(txt, True, c)
+ self.x, self.y = x, y
+ self.c = c
+ self.rect = pygame.Rect(rect)
+ def draw(self, screen):
+ if self.rect.collidepoint(framework.mousePos):
+ screen.blit(self.img, (self.x, self.y))
+
+class TxtField:
+ def __init__(self, x, y, w, h):
+ self.x, self.y = x, y
+ self.w, self.h = w, h
+ self.pwd = '/'
+ self.placeholder = ['%s# ' % self.pwd]
+ self.txtBuffer = []
+ self.content = []
+ # MAGIC #
+ self.caps = { '`': '~', '1': '!', '2': '@', '3': '#', '4': '$', '5': '%', '6': '^', '7': '&', '8': '*', '9': '(', '0': ')', '-': '_', '=': '+', '[': '{', ']': '}', '\\': '|', ';': ':', '\'': '"', ',': '<', '.': '>', '/': '?', 'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E', 'f': 'F', 'g': 'G', 'h': 'H', 'i': 'I', 'j': 'J', 'k': 'K', 'l': 'L', 'm': 'M', 'n': 'N', 'o': 'O', 'p': 'P', 'q': 'Q', 'r': 'R', 's': 'S', 't': 'T', 'u': 'U', 'v': 'V', 'w': 'W', 'x': 'X', 'y': 'Y', 'z': 'Z' }
+ self.shift, self.capsLock = False, False
+ self.currentLine, self.loc, self.maxIndex = 0, 0, 0
+ self.cLineStr = ""
+ self.raster = pygame.font.Font("res/Perfect_DOS_VGA_437.ttf", 16)
+ def wrap(self, txtBuffer):
+ lines = []
+ ptr = 0
+ prev_i = 0
+ ph = self.placeholder[ptr]
+ if self.loc > self.maxIndex:
+ self.loc = self.maxIndex
+ if self.loc < 0: self.loc = 0
+ if self.maxIndex < 0: self.loc = 0
+ for i in range(len(txtBuffer)):
+ if txtBuffer[i] == '\n':
+ self.currentLine = i + 1
+ lines.append(ph)
+ ptr += 1
+ ph = self.placeholder[ptr]
+ prev_i = i
+ elif txtBuffer[i] == '\r' or (i != 0 and (i - prev_i) % self.w == 0):
+ ph += txtBuffer[i] if txtBuffer[i] not in ('\n', '\r', ' ', '') else ''
+ self.currentLine = i + 1
+ lines.append(ph)
+ ph = ''
+ prev_i = i
+ elif txtBuffer[i] == '\t':
+ ph += ' '
+ else:
+ ph += txtBuffer[i]
+ lines.append(ph)
+ return lines
+ def draw(self, screen, lines, c = (255, 255, 255), y = 0):
+ for line in lines:
+ img = self.raster.render(line, True, c)
+ screen.blit(img, (self.x, y))
+ y += 16
+ def cd(self, dir_name):
+ def process_dir(dr: str):
+ for d in dr.split('/'):
+ if d:
+ if d[0] == '.':
+ if d == '..':
+ self.pwd = ""
+ try: nTemp.pop()
+ except: pass
+ for i in nTemp:
+ if i:
+ self.pwd += '/' + i
+ self.pwd += '/'
+ elif (d + '/') in ls(self.pwd, [], False):
+ self.pwd += d + '/'
+ else:
+ print("cd: %s: no such directory." % d, end='\r')
+ return
+ temp = self.pwd.split('/')
+ nTemp = []
+ self.pwd = ""
+ for i in temp:
+ if i:
+ self.pwd += '/' + i
+ nTemp.append(i)
+ self.pwd += '/'
+ if dir_name[0] == '/':
+ self.pwd = '/'
+ dir_name = dir_name[1:]
+ process_dir(dir_name)
+ def vis(self, args):
+ if not args:
+ print("vis: missing argument.\rUsage: vis ", end='\r')
+ if type(args) == type([]):
+ print("vis: too many arguments.\rUsage: vis ", end='\r')
+ if '/' in args:
+ print("No.", end='\r')
+ return
+ while True:
+ rm(self.pwd, [args])
+ files = open("files.img", 'a+', encoding="ISO-8859-1")
+ files.write("\n\n!LOC=" + self.pwd + "\n")
+ files.write("!FNAME=" + args + "\n")
+ self.txtBuffer.append('\r')
+ self.txtBuffer.append('\r')
+ while True:
+ for event in pygame.event.get():
+ if event.type == pygame.KEYDOWN:
+ self.txtBuffer = []
+ self.content = []
+ self.keyDown(event.key, True)
+ files.write(self.cLineStr + '\n')
+ framework.launch()
+ elif event.type == pygame.KEYUP:
+ if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT:
+ self.shift = False
+ elif event.key == pygame.K_CAPSLOCK:
+ self.capsLock = 1 - self.capsLock
+ elif event.type == pygame.QUIT:
+ pygame.quit()
+ sys.exit()
+ framework.launch()
+ files.close()
+ def exec_cmd(self, on_scr):
+ cmd = on_scr.split()
+ try: main = cmd.pop(0)
+ except: main = None
+ output = io.StringIO()
+ with redirect_stdout(output):
+ if main:
+ if main == 'cd': self.cd(cmd[0])
+ elif main == 'vis': self.vis(cmd[0])
+ else:
+ try: exec("%s('%s', %s)" % (main, self.pwd, str(cmd)))
+ except: print("%s: command not found" % on_scr, end='')
+ else: pass
+ self.txtBuffer.append('\r')
+ self.txtBuffer += list(output.getvalue().rstrip('\n'))
+ self.placeholder.append('%s# ' % self.pwd)
+ def keyUp(self, key):
+ if key == pygame.K_LSHIFT or key == pygame.K_RSHIFT:
+ self.shift = False
+ elif key == pygame.K_CAPSLOCK:
+ self.capsLock = 1 - self.capsLock
+ def keyDown(self, key, isVis = False):
+ if key == pygame.K_BACKSPACE:
+ if (self.txtBuffer and self.txtBuffer[-1] != '\n') or not self.txtBuffer:
+ try:
+ if self.loc - 1 != -1:
+ self.txtBuffer.pop(self.loc - 1)
+ self.maxIndex -= 1
+ self.loc -= 1
+ except: pass
+ elif key == pygame.K_DELETE:
+ try:
+ if self.loc > self.maxIndex:
+ self.loc = self.maxIndex
+ self.txtBuffer.pop(self.currentLine + self.loc)
+ self.maxIndex -= 1
+ except: pass
+ elif key == pygame.K_RETURN:
+ cmd = ''
+ self.loc = 0
+ self.maxIndex = 0
+ i = -1
+ if isVis:
+ while len(self.txtBuffer) > - i - 1 and self.txtBuffer[i] != '\r':
+ cmd = self.txtBuffer[i] + cmd
+ i -= 1
+ else:
+ while len(self.txtBuffer) > - i - 1 and self.txtBuffer[i] != '\n':
+ cmd = self.txtBuffer[i] + cmd
+ i -= 1
+ if isVis:
+ self.cLineStr = cmd
+ self.txtBuffer.append('\r')
+ return
+ else:
+ self.exec_cmd(cmd)
+ self.txtBuffer.append('\n')
+ framework.launch()
+ elif key == pygame.K_TAB:
+ for i in range(4):
+ self.txtBuffer.append(' ')
+ self.loc += 1
+ elif key == pygame.K_LSHIFT or key == pygame.K_RSHIFT:
+ self.shift = True
+ elif key == pygame.K_CAPSLOCK:
+ self.capsLock = 1 - self.capsLock
+ elif key == pygame.K_LEFT:
+ if self.loc != 0:
+ self.loc -= 1
+ elif key == pygame.K_RIGHT:
+ if self.loc + 1 <= self.maxIndex:
+ self.loc += 1
+ else:
+ if 32 <= key <= 126:
+ if (key == 39 or 44 <= key <= 57 or key == 59 or key == 61 or key == 96 or 91 <= key <= 93) and self.shift:
+ self.txtBuffer.insert(self.loc, self.caps[chr(key)])
+ elif 97 <= event.key <= 122 and (self.shift or self.capsLock):
+ self.txtBuffer.insert(self.loc, self.caps[chr(key)])
+ else:
+ self.txtBuffer.insert(self.loc, chr(key))
+ self.loc += 1
+ self.maxIndex += 1
+ framework.launch()
+ print(self.txtBuffer, self.loc, self.maxIndex)
+
+class Secret:
+ def __init__(self, rect, dialogID):
+ self.rect = pygame.Rect(rect)
+ self.dialogID = dialogID
+ def mouseDown(self, pos, button):
+ if self.rect.collidepoint(pos):
+ framework.dialogs.pop()
+
+class DlgStatus(Enum):
+ INFO = 0
+ WARNING = 1
+ ERROR = 2
+
+class Dialog:
+ def __init__(self, title, content, status=DlgStatus.INFO):
+ self.img = pygame.image.load("res/dialog/dialog.png")
+ self.icon = pygame.transform.scale(pygame.image.load("res/dialog/" + str(status) + ".bmp"), (32, 32))
+ self.icon.set_colorkey((255, 0, 255))
+ self.raster = pygame.font.Font("res/Perfect_DOS_VGA_437.ttf", 16)
+ self.title = self.raster.render(title, True, (255, 255, 255))
+ self.content = self.wrap(content, 35)
+ self.dialogID = 0
+ self.w, self.h = self.img.get_size()
+ self.x, self.y = 322, 284
+ self.closeBtn = None
+ def wrap(self, txt, w):
+ processed = []
+ temp = ""
+ for i in range(txt.__len__()):
+ if i != 0 and i % w == 0:
+ processed.append(self.raster.render(temp, True, (0, 0, 0)))
+ temp = ""
+ temp += txt[i]
+ processed.append(self.raster.render(temp, True, (0, 0, 0)))
+ return processed
+ def draw(self, screen):
+ screen.blit(self.img, (self.x, self.y))
+ screen.blit(self.title, (self.x + 10, self.y + 6))
+ screen.blit(self.icon, (self.x + 30, self.y + 90))
+ for i in range(len(self.content)):
+ screen.blit(self.content[i], (self.x + 80, self.y + 35 + i * 16))
+ def mouseDown(self, pos, button):
+ self.closeBtn.mouseDown(pos, button)
+
+class SnakeGame:
+ def __init__(self):
+ self.tileWidth = 32
+ self.x0, self.y0 = 192, 144
+ self.snakeLen = 1
+ self.snakePos = []
+ self.direction = 0
+ self.speedX = (1, 0, -1, 0)
+ self.speedY = (0, 1, 0, -1)
+ self.sx, self.sy = 0, 0
+ self.ix, self.iy = random.randint(0, 19), random.randint(0, 14)
+ self.lost = 0
+ self.bigFont = pygame.font.Font("res/Perfect_DOS_VGA_437.ttf", 100)
+ self.smallFont = pygame.font.Font("res/Perfect_DOS_VGA_437.ttf", 60)
+ self.bg = pygame.Surface((640, 480))
+ self.bg.fill((255, 255, 255))
+ self.gameOver = self.bigFont.render("Game Over!", True, (255, 255, 255))
+ self.restart = self.smallFont.render("PRESS R TO RESTART", True, (255, 255, 255))
+ def draw(self, canvas):
+ canvas.fill((40, 40, 40))
+ if self.lost == 1:
+ canvas.blit(self.gameOver, (250, 200))
+ canvas.blit(self.restart, (230, 300))
+ return
+ canvas.blit(self.bg, (self.x0, self.y0))
+ for pos in self.snakePos[-self.snakeLen:]:
+ pygame.draw.rect(canvas, (60, 110, 5), (self.x0 + pos[0] * self.tileWidth, self.y0 + pos[1] * self.tileWidth, self.tileWidth, self.tileWidth))
+ pygame.draw.rect(canvas, (190, 30, 50), (self.x0 + self.ix * self.tileWidth, self.y0 + self.iy * self.tileWidth, self.tileWidth, self.tileWidth))
+ def move(self):
+ if self.lost == 1:
+ return
+ self.sx = (self.sx + self.speedX[self.direction]) % 20
+ self.sy = (self.sy + self.speedY[self.direction]) % 15
+ if (self.sx, self.sy) in self.snakePos[-self.snakeLen:]:
+ self.lost = 1
+ self.snakePos.append((self.sx, self.sy))
+ if self.sx == self.ix and self.sy == self.iy:
+ self.ix, self.iy = random.randint(0, 19), random.randint(0, 14)
+ self.snakeLen += 1
+ def keyDown(self, key):
+ if key == pygame.K_UP:
+ self.direction = 3
+ if key == pygame.K_DOWN:
+ self.direction = 1
+ if key == pygame.K_LEFT:
+ self.direction = 2
+ if key == pygame.K_RIGHT:
+ self.direction = 0
+ if key == pygame.K_r and self.lost == 1:
+ self.snakeLen = 1
+ self.snakePos = []
+ self.sx, self.sy = 0, 0
+ self.ix, self.iy = random.randint(0, 19), random.randint(0, 14)
+ self.lost = 0
+
+framework = Kernel()
+bg = App("res/clouds.jpg")
+term = App("res/blank.jpg")
+snake = App("res/blank.jpg")
+snake.enableCanvas()
+snakeGame = SnakeGame()
+framework.appID = bg.appID
+framework.addApp(bg)
+framework.addApp(term)
+framework.addApp(snake)
+framework.addDialog(Dialog("Hey there!", "Welcome to Winnux 58!"))
+bg.addButton(Button("res/button/txt_btn.bmp", 20, 20, term.appID, font=framework.raster, content="TERMINAL"))
+bg.addButton(Button("res/button/txt_btn.bmp", 20, 60, snake.appID, font=framework.raster, content="SNAKE"))
+term.addButton(Button("res/button/txt_btn.bmp", width // 2 - 35, 20, bg.appID, font=framework.raster, content="CLOSE"))
+snake.addButton(Button("res/button/txt_btn.bmp", width // 2 - 35, 20, bg.appID, font=framework.raster, content="CLOSE"))
+term.enableTxtField(50, 60, 100, 40)
+
+while True:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ pygame.quit()
+ sys.exit()
+ if event.type == pygame.KEYDOWN:
+ framework.keyDown(event.key)
+ elif event.type == pygame.KEYUP:
+ framework.keyUp(event.key)
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ framework.mouseDown(event.pos, event.button)
+ elif event.type == pygame.MOUSEBUTTONUP:
+ framework.mouseUp(event.pos, event.button)
+ elif event.type == pygame.MOUSEMOTION:
+ framework.mouseMotion(event.pos)
+ framework.launch()
\ No newline at end of file
diff --git a/wiki/FAQ.md b/wiki/FAQ.md
new file mode 100644
index 0000000..77a0892
--- /dev/null
+++ b/wiki/FAQ.md
@@ -0,0 +1,14 @@
+## What do I need?
+You will need:
+- A working Python interpreter, version 3 and up
+- The [pygame module](pygame.org) installed
+
+## How do I run the program?
+Run `stub.py` either by double-clicking on it (in Windows) or using the command line.
+
+## FAQ for `vis`
+### How do I use `vis`?
+When you open it up through the virtual terminal, you will be put in *input mode*, which means whatever you type will be written into the file. `vis` is a *line-based* editor, meaning you can only edit the line you're on.
+
+### Where do the files go?
+Everything you created in Winnux 58 will go into the `files.img` *virtual disk*. You can view them using the `cat` command in the virtual terminal.
\ No newline at end of file
diff --git a/wiki/FAQ.pdf b/wiki/FAQ.pdf
new file mode 100644
index 0000000..d22410c
Binary files /dev/null and b/wiki/FAQ.pdf differ