mirror of
https://gitlab.com/niansa/minituxi.git
synced 2025-03-06 20:49:18 +01:00
Lots and lots of fixups and cleanups, basic framebuffer stuff
This commit is contained in:
parent
a69319e942
commit
006d62ed76
9 changed files with 2220 additions and 41 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
bin
|
||||
bzImage
|
||||
CMakeLists.txt.user
|
||||
initramfs.cpio.gz
|
27
CMakeLists.txt
Normal file
27
CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Created by and for Qt Creator This file was created for editing the project sources only.
|
||||
# You may attempt to use it for building too, by modifying this file here.
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(minituxi)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
|
||||
include_directories(
|
||||
include
|
||||
)
|
||||
|
||||
set(SRCS
|
||||
include/defs.h
|
||||
include/system.hpp
|
||||
include/font.hpp
|
||||
src/main.cpp
|
||||
src/system.cpp
|
||||
src/font.cpp
|
||||
)
|
||||
|
||||
|
||||
add_executable(${CMAKE_PROJECT_NAME} ${SRCS})
|
||||
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC pthread)
|
6
Makefile
6
Makefile
|
@ -1,10 +1,14 @@
|
|||
all: genimg
|
||||
|
||||
compile:
|
||||
mkdir -p bin
|
||||
utils/compile.sh
|
||||
|
||||
genimg: compile
|
||||
utils/genimg.sh
|
||||
|
||||
test:
|
||||
qemu-system-x86_64 -hda /dev/null -m 64 -kernel bzImage -initrd initramfs.cpio.gz -append 'quiet rdinit=/main'
|
||||
qemu-system-x86_64 -hda /dev/null -m 128 -kernel bzImage -initrd initramfs.cpio.gz -append 'quiet rdinit=/main'
|
||||
|
||||
clean:
|
||||
cat .gitignore | xargs rm -rfv
|
||||
|
|
21
include/font.hpp
Normal file
21
include/font.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <span>
|
||||
|
||||
|
||||
|
||||
namespace Font {
|
||||
constexpr unsigned int height = 16,
|
||||
width = 8;
|
||||
constexpr unsigned int size = height * width;
|
||||
|
||||
using Letter = std::span<const bool>;
|
||||
|
||||
|
||||
extern const bool fontBitmapUnk[size];
|
||||
|
||||
|
||||
|
||||
Letter getChar(const char character);
|
||||
inline Letter getUnk() {
|
||||
return Letter{fontBitmapUnk, size};
|
||||
}
|
||||
}
|
|
@ -1,66 +1,233 @@
|
|||
#ifndef _SYSTEM_HPP
|
||||
#define _SYSTEM_HPP
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
|
||||
|
||||
namespace System {
|
||||
[[noreturn]]
|
||||
void lockup();
|
||||
|
||||
namespace get_mem {
|
||||
size_t total() {
|
||||
// Get sysinfo
|
||||
struct sysinfo i;
|
||||
sysinfo(&i);
|
||||
// Get total ram
|
||||
return i.totalram;
|
||||
}
|
||||
|
||||
size_t free() {
|
||||
// Get sysinfo
|
||||
struct sysinfo i;
|
||||
sysinfo(&i);
|
||||
// Get available ram
|
||||
return i.freeram;
|
||||
}
|
||||
|
||||
size_t used() {
|
||||
// Get sysinfo
|
||||
struct sysinfo i;
|
||||
sysinfo(&i);
|
||||
// Get used ram
|
||||
return i.totalram - i.freeram;
|
||||
[[noreturn]]
|
||||
void critical_stop(std::string_view msg);
|
||||
|
||||
template<typename T>
|
||||
inline T check_sysc(T v, std::string_view msg = "Syscall failed") {
|
||||
if (v < 0) {
|
||||
critical_stop(msg);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
class SysInfo {
|
||||
struct sysinfo i;
|
||||
|
||||
public:
|
||||
SysInfo() {
|
||||
check_sysc(sysinfo(&i), "Failed to obtain sysinfo");
|
||||
}
|
||||
|
||||
const struct sysinfo& get() const {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Mounts {
|
||||
public:
|
||||
Mounts() {
|
||||
std::filesystem::create_directory("/dev");
|
||||
mount("devtmpfs", "/dev", "devtmpfs", 0, 0);
|
||||
std::filesystem::create_directory("/proc");
|
||||
check_sysc(mount("devtmpfs", "/dev", "devtmpfs", 0, 0), "Failed to initialize devices filesystem");
|
||||
check_sysc(mount("proc", "/proc", "proc", 0, 0), "Failed to initialize process filesystem");
|
||||
}
|
||||
|
||||
~Mounts() {
|
||||
umount("/dev");
|
||||
check_sysc(umount("/dev"), "Failed to deinitialize devices filesystem");
|
||||
check_sysc(umount("/proc"), "Failed to deinitialize process filesystem");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Console {
|
||||
class TermAntiFlagger {
|
||||
termios termbak;
|
||||
public:
|
||||
Console(tcflag_t antiflags) {
|
||||
TermAntiFlagger(tcflag_t antiflags) {
|
||||
termios thisterm;
|
||||
tcgetattr(0, &thisterm);
|
||||
termbak = thisterm;
|
||||
thisterm.c_lflag &= ~antiflags;
|
||||
tcsetattr(0, TCSANOW, &thisterm);
|
||||
}
|
||||
~Console() {
|
||||
~TermAntiFlagger() {
|
||||
tcsetattr(0, TCSANOW, &termbak);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Framebuffer {
|
||||
using pixType = uint32_t;
|
||||
|
||||
struct fb_fix_screeninfo finfo;
|
||||
struct fb_var_screeninfo vinfo;
|
||||
int fb_fd;
|
||||
pixType *buf; // Slow real buffer
|
||||
pixType *dBuf; // Fast double buffer
|
||||
size_t bufSize; // In bytes!!!
|
||||
size_t bufArrSize; // In pixels
|
||||
|
||||
public:
|
||||
Framebuffer() {
|
||||
// Open framebuffer
|
||||
fb_fd = check_sysc(open("/dev/fb0", O_RDWR), "Unable to access framebuffer");
|
||||
// Collect information
|
||||
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
|
||||
ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);
|
||||
bufArrSize = vinfo.xres * vinfo.yres;
|
||||
bufSize = sizeof(pixType) * bufArrSize;
|
||||
// Map memory
|
||||
buf = reinterpret_cast<pixType*>(mmap(nullptr, bufSize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0));
|
||||
dBuf = new pixType[bufSize];
|
||||
}
|
||||
~Framebuffer() {
|
||||
// Unmap memory
|
||||
munmap(buf, bufSize);
|
||||
delete []dBuf;
|
||||
// Close framebuffer
|
||||
close(fb_fd);
|
||||
}
|
||||
|
||||
const auto& get_finfo() const {
|
||||
return finfo;
|
||||
}
|
||||
const auto& get_vinfo() const {
|
||||
return vinfo;
|
||||
}
|
||||
|
||||
bool inBounds(size_t idx) {
|
||||
return idx < bufArrSize;
|
||||
}
|
||||
|
||||
pixType& operator[](size_t idx) {
|
||||
return dBuf[idx];
|
||||
}
|
||||
|
||||
void display() {
|
||||
memcpy(buf, dBuf, bufSize);
|
||||
}
|
||||
|
||||
void draw(uint x, uint y, pixType color) {
|
||||
if (x < vinfo.xres && y < vinfo.yres) {
|
||||
dBuf[y * vinfo.xres + x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
void fill(pixType color) {
|
||||
for (size_t idx = 0; inBounds(idx); idx++) {
|
||||
dBuf[idx] = color;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_text(std::string_view str, uint x, uint y, pixType color = 0xFFFFFF);
|
||||
|
||||
void draw_horizontal_line(uint x1, uint x2, uint y, pixType color) {
|
||||
for (uint i = x1; i < x2; i++) {
|
||||
draw(i, y, color);
|
||||
}
|
||||
}
|
||||
void draw_vertical_line(uint x, uint y1, uint y2, pixType color) {
|
||||
for (uint i = y1; i < y2; i++) {
|
||||
draw(x, i, color);
|
||||
}
|
||||
}
|
||||
void draw_line(uint x1, uint y1, uint x2, uint y2, pixType color);
|
||||
|
||||
void draw_circle(double cx, double cy, uint radius, pixType color, bool filled = false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Mouse {
|
||||
int mouse_fd;
|
||||
|
||||
public:
|
||||
struct Event {
|
||||
struct input_event base;
|
||||
|
||||
enum {
|
||||
MOUSEX,
|
||||
MOUSEY,
|
||||
WHEEL,
|
||||
BUTTON
|
||||
} type;
|
||||
|
||||
int32_t value;
|
||||
|
||||
Event(struct input_event& base) : base(base) {
|
||||
switch (base.type) {
|
||||
case 2: {
|
||||
switch (base.code) {
|
||||
case 0: type = MOUSEX; break;
|
||||
case 1: type = MOUSEY; break;
|
||||
case 11: type = WHEEL; break;
|
||||
}
|
||||
} break;
|
||||
case 4: type = BUTTON; break;
|
||||
}
|
||||
value = base.value;
|
||||
}
|
||||
};
|
||||
|
||||
Mouse() {
|
||||
// Find mouse
|
||||
std::filesystem::path mouseEventPath;
|
||||
for (const auto& f : std::filesystem::directory_iterator("/dev/input/by-path/")) {
|
||||
auto& path = f.path();
|
||||
if (f.path().filename().generic_string().ends_with("-event-mouse")) {
|
||||
mouseEventPath = f.path();
|
||||
}
|
||||
}
|
||||
// Check that a mouse was found
|
||||
if (mouseEventPath.empty()) {
|
||||
mouse_fd = -1;
|
||||
} else {
|
||||
// Open mice
|
||||
mouse_fd = check_sysc(open(mouseEventPath.c_str(), O_RDWR), "Unable to access mouse");
|
||||
// Gain exclusive access
|
||||
check_sysc(ioctl(mouse_fd, EVIOCGRAB, 1), "Failed to gain exclusive access over mouse");
|
||||
}
|
||||
}
|
||||
|
||||
bool connected() {
|
||||
return mouse_fd != -1;
|
||||
}
|
||||
|
||||
Event event_wait() {
|
||||
if (!connected()) {
|
||||
lockup();
|
||||
}
|
||||
struct input_event ev;
|
||||
read(mouse_fd, &ev, sizeof(ev));
|
||||
return Event(ev);
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
1763
src/font.cpp
Normal file
1763
src/font.cpp
Normal file
File diff suppressed because it is too large
Load diff
89
src/main.cpp
89
src/main.cpp
|
@ -1,21 +1,86 @@
|
|||
#include "system.hpp"
|
||||
#include "font.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include "system.hpp"
|
||||
using namespace std;
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <exception>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
{
|
||||
// Initialisations
|
||||
Mounts mounts;
|
||||
struct UMain {
|
||||
std::mutex mutex;
|
||||
System::Framebuffer fb;
|
||||
System::Mouse mouse;
|
||||
struct {uint x = 100, y = 100;} mousePos;
|
||||
|
||||
// Print memory usage
|
||||
cout << "Total: " << get_mem::total() / 1000000 << " MB" << endl
|
||||
<< "Used: " << get_mem::used() / 1000000 << " MB" << endl
|
||||
<< "Free: " << get_mem::free() / 1000000 << " MB" << endl;
|
||||
void poll() {
|
||||
// Update mouse position
|
||||
auto ev = mouse.event_wait();
|
||||
mutex.lock();
|
||||
switch (ev.type) {
|
||||
case System::Mouse::Event::MOUSEX: mousePos.x += ev.value; break;
|
||||
case System::Mouse::Event::MOUSEY: mousePos.y += ev.value; break;
|
||||
default: break;
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
while (1);
|
||||
void pollLoop() {
|
||||
while (true) {
|
||||
poll();
|
||||
}
|
||||
}
|
||||
|
||||
void drawLoop() {
|
||||
while (true) {
|
||||
mutex.unlock();
|
||||
std::this_thread::sleep_for(15ms);
|
||||
mutex.lock();
|
||||
// Clear screen
|
||||
fb.fill(0x000000);
|
||||
// Draw stuff
|
||||
fb.draw_circle(100, 100, 50, 0xFF0000, true);
|
||||
fb.draw_horizontal_line(0, 100, 100, 0x0000FF);
|
||||
fb.draw_text("Hello world!", 250, 250);
|
||||
// Draw mouse
|
||||
fb.draw_circle(mousePos.x, mousePos.y, 5, 0xFFFFFF, true);
|
||||
fb.draw_text("This is the cursor", mousePos.x + 20, mousePos.y - Font::height / 2, 0x00FF00);
|
||||
// Display
|
||||
fb.display();
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
std::thread pollThread([this] () {
|
||||
pollLoop();
|
||||
});
|
||||
drawLoop();
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "\033[H\033[J" << std::flush;
|
||||
|
||||
// Initialisations if PID 1 then run
|
||||
try {
|
||||
if (getpid() == 1) {
|
||||
System::Mounts mounts;
|
||||
UMain().run();
|
||||
} else {
|
||||
UMain().run();
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
System::critical_stop(e.what());
|
||||
} catch (...) {
|
||||
System::critical_stop("Unknown");
|
||||
}
|
||||
|
||||
// Lock up
|
||||
System::lockup();
|
||||
}
|
||||
|
|
128
src/system.cpp
Normal file
128
src/system.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "system.hpp"
|
||||
#include "font.hpp"
|
||||
|
||||
|
||||
|
||||
namespace System {
|
||||
[[noreturn]]
|
||||
void lockup() {
|
||||
std::cout << std::flush;
|
||||
std::cerr << std::flush;
|
||||
if (getpid() == 1) {
|
||||
while(1) usleep(-1);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void critical_stop(std::string_view msg) {
|
||||
std::cerr << "\n\n\n"
|
||||
"A critical stop has occured: \n"
|
||||
"Message: " << msg << "\n"
|
||||
"Errno: " << strerror(errno) << std::endl;
|
||||
lockup();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Framebuffer::draw_text(std::string_view str, uint x, uint y, pixType color) {
|
||||
for (const auto c : str) {
|
||||
// Get bitmap
|
||||
auto bitmap = Font::getChar(c);
|
||||
// Render
|
||||
int rx = 0;
|
||||
int ry = 0;
|
||||
for (const auto pixel : bitmap) {
|
||||
if (rx == Font::width) {
|
||||
rx = 0;
|
||||
ry++;
|
||||
}
|
||||
if (ry == Font::height) {
|
||||
break;
|
||||
}
|
||||
if (pixel) {
|
||||
draw(x + rx, y + ry, color);
|
||||
}
|
||||
rx++;
|
||||
}
|
||||
// Increase x
|
||||
x += Font::width;
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::draw_line(uint x1, uint y1, uint x2, uint y2, pixType pixel) {
|
||||
// Deltas
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
// Absolute deltas
|
||||
uint dxabs = abs(dx);
|
||||
uint dyabs = abs(dy);
|
||||
// Signed deltas
|
||||
int sdx = (dx>0)?1:-1;
|
||||
int sdy = (dy>0)?1:-1;
|
||||
// Misc stuff
|
||||
int x = dyabs / 2;
|
||||
int y = dxabs / 2;
|
||||
int px = x1;
|
||||
int py = y1;
|
||||
|
||||
if (dxabs>=dyabs) {
|
||||
for (int i = 0; i < dxabs; i++) {
|
||||
y += dyabs;
|
||||
if (y >= dxabs) {
|
||||
y -= dxabs;
|
||||
py += sdy;
|
||||
}
|
||||
px += sdx;
|
||||
draw(px,py,pixel);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < dyabs; i++) {
|
||||
x += dxabs;
|
||||
if (x >= dyabs) {
|
||||
x -= dyabs;
|
||||
px += sdx;
|
||||
}
|
||||
py += sdy;
|
||||
draw(px,py,pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Framebuffer::draw_circle(double cx, double cy, uint radius, pixType color, bool filled) {
|
||||
auto plot4points = [&] (double x, double y) {
|
||||
if (filled) {
|
||||
draw_horizontal_line(cx - x, cx + x, cy + y, color);
|
||||
draw_horizontal_line(cx - x, cx + x, cy - y, color);
|
||||
} else {
|
||||
draw(cx + x, cy + y, color);
|
||||
draw(cx - x, cy + y, color);
|
||||
draw(cx + x, cy - y, color);
|
||||
draw(cx - x, cy - y, color);
|
||||
}
|
||||
};
|
||||
auto plot8points = [&] (double x, double y) {
|
||||
plot4points(x, y);
|
||||
plot4points(y, x);
|
||||
};
|
||||
|
||||
int error = -radius;
|
||||
double x = radius;
|
||||
double y = 0;
|
||||
|
||||
while (x >= y) {
|
||||
plot8points(x, y);
|
||||
|
||||
error += y;
|
||||
y++;
|
||||
error += y;
|
||||
|
||||
if (error >= 0) {
|
||||
error += -x;
|
||||
x--;
|
||||
error += -x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue