Compare commits
	
		
			21 Commits
		
	
	
		
			ae3ae6fd0e
			...
			530c259e71
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 530c259e71 | |
|  | 843a6851c4 | |
|  | 234051d6bc | |
|  | 981c0eb8bc | |
|  | 1066855532 | |
|  | f2d6518311 | |
|  | 765ccfa18c | |
|  | 201aee3119 | |
|  | 65f299038d | |
|  | bd1290706a | |
|  | 939cbf46e4 | |
|  | aec5a09caf | |
|  | 6346d1b6c7 | |
|  | 05ee242b80 | |
|  | 64be3f05a3 | |
|  | cfdce9be61 | |
|  | 446220494e | |
|  | f12ffa92a0 | |
|  | b2a4797d16 | |
|  | 8bfacb0091 | |
|  | 0501f3bd99 | 
|  | @ -21,15 +21,17 @@ | |||
| #define dwarnln(...)											\ | ||||
| 	do {														\ | ||||
| 		BAN::Formatter::print(__debug_putchar, "\e[33m");		\ | ||||
| 		dprintln(__VA_ARGS__);								\ | ||||
| 		BAN::Formatter::print(__debug_putchar, __VA_ARGS__);	\ | ||||
| 		BAN::Formatter::print(__debug_putchar, "\e[m");			\ | ||||
| 		fflush(stddbg);											\ | ||||
| 	} while(false) | ||||
| 
 | ||||
| #define derrorln(...)											\ | ||||
| 	do {														\ | ||||
| 		BAN::Formatter::print(__debug_putchar, "\e[31m");		\ | ||||
| 		dprintln(__VA_ARGS__);								\ | ||||
| 		BAN::Formatter::print(__debug_putchar, __VA_ARGS__);	\ | ||||
| 		BAN::Formatter::print(__debug_putchar, "\e[m");			\ | ||||
| 		fflush(stddbg);											\ | ||||
| 	} while(false) | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ namespace BAN | |||
| 
 | ||||
| 	protected: | ||||
| 		RefCounted() = default; | ||||
| 		~RefCounted() { ASSERT(m_ref_count == 0); } | ||||
| 		virtual ~RefCounted() { ASSERT(m_ref_count == 0); } | ||||
| 
 | ||||
| 	private: | ||||
| 		mutable uint32_t m_ref_count = 1; | ||||
|  |  | |||
|  | @ -34,8 +34,10 @@ namespace BAN | |||
| 	template<typename T1, typename T2> struct either_or<true, T1, T2> { using type = T1; }; | ||||
| 	template<bool B, typename T1, typename T2> using either_or_t = typename either_or<B, T1, T2>::type; | ||||
| 
 | ||||
| 	struct true_type { static constexpr bool value = true; }; | ||||
| 	struct false_type { static constexpr bool value = false; }; | ||||
| 	template<typename T, T V> struct integral_constant { static constexpr T value = V; }; | ||||
| 	template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value; | ||||
| 	using true_type = integral_constant<bool, true>; | ||||
| 	using false_type = integral_constant<bool, false>; | ||||
| 
 | ||||
| 	template<typename T, typename S>	struct is_same			: false_type {}; | ||||
| 	template<typename T>				struct is_same<T, T>	: true_type {}; | ||||
|  | @ -87,9 +89,6 @@ namespace BAN | |||
| 	template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); }; | ||||
| 	template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value; | ||||
| 
 | ||||
| 	template<typename T, T V> struct integral_constant { static constexpr T value = V; }; | ||||
| 	template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value; | ||||
| 
 | ||||
| 	namespace detail | ||||
| 	{ | ||||
| 		template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); }; | ||||
|  |  | |||
|  | @ -21,20 +21,21 @@ namespace BAN::UTF8 | |||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	constexpr uint32_t to_codepoint(uint8_t* bytes) | ||||
| 	template<typename T> requires (sizeof(T) == 1) | ||||
| 	constexpr uint32_t to_codepoint(const T* bytes) | ||||
| 	{ | ||||
| 		uint32_t length = byte_length(bytes[0]); | ||||
| 
 | ||||
| 		for (uint32_t i = 1; i < length; i++) | ||||
| 			if ((bytes[i] & 0xC0) != 0x80) | ||||
| 			if (((uint8_t)bytes[i] & 0xC0) != 0x80) | ||||
| 				return UTF8::invalid; | ||||
| 
 | ||||
| 		switch (length) | ||||
| 		{ | ||||
| 			case 1: return ((bytes[0] & 0x80) != 0x00) ? UTF8::invalid :   bytes[0]; | ||||
| 			case 2: return ((bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : ((bytes[0] & 0x1F) <<  6) |  (bytes[1] & 0x3F); | ||||
| 			case 3: return ((bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) <<  6) |  (bytes[2] & 0x3F); | ||||
| 			case 4: return ((bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) | ((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F); | ||||
| 			case 1: return (((uint8_t)bytes[0] & 0x80) != 0x00) ? UTF8::invalid :   (uint8_t)bytes[0]; | ||||
| 			case 2: return (((uint8_t)bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x1F) <<  6) |  ((uint8_t)bytes[1] & 0x3F); | ||||
| 			case 3: return (((uint8_t)bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x0F) << 12) | (((uint8_t)bytes[1] & 0x3F) <<  6) |  ((uint8_t)bytes[2] & 0x3F); | ||||
| 			case 4: return (((uint8_t)bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x07) << 18) | (((uint8_t)bytes[1] & 0x3F) << 12) | (((uint8_t)bytes[2] & 0x3F) << 6) | ((uint8_t)bytes[3] & 0x3F); | ||||
| 		} | ||||
| 
 | ||||
| 		return UTF8::invalid; | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ namespace BAN | |||
| 	class Weakable | ||||
| 	{ | ||||
| 	public: | ||||
| 		~Weakable() | ||||
| 		virtual ~Weakable() | ||||
| 		{ | ||||
| 			if (m_link) | ||||
| 				m_link->invalidate(); | ||||
|  | @ -82,7 +82,7 @@ namespace BAN | |||
| 
 | ||||
| 		RefPtr<T> lock() | ||||
| 		{ | ||||
| 			if (m_link->valid()) | ||||
| 			if (valid()) | ||||
| 				return m_link->lock(); | ||||
| 			return nullptr; | ||||
| 		} | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ add_subdirectory(bootloader) | |||
| add_subdirectory(BAN) | ||||
| add_subdirectory(libc) | ||||
| add_subdirectory(LibELF) | ||||
| add_subdirectory(LibFont) | ||||
| add_subdirectory(LibGUI) | ||||
| add_subdirectory(LibInput) | ||||
| add_subdirectory(userspace) | ||||
|  | @ -35,6 +36,7 @@ add_custom_target(headers | |||
| 	DEPENDS ban-headers | ||||
| 	DEPENDS libc-headers | ||||
| 	DEPENDS libelf-headers | ||||
| 	DEPENDS libfont-headers | ||||
| 	DEPENDS libgui-headers | ||||
| 	DEPENDS libinput-headers | ||||
| ) | ||||
|  | @ -45,6 +47,7 @@ add_custom_target(install-sysroot | |||
| 	DEPENDS libc-install | ||||
| 	DEPENDS userspace-install | ||||
| 	DEPENDS libelf-install | ||||
| 	DEPENDS libfont-install | ||||
| 	DEPENDS libgui-install | ||||
| 	DEPENDS libinput-install | ||||
| ) | ||||
|  |  | |||
|  | @ -0,0 +1,24 @@ | |||
| cmake_minimum_required(VERSION 3.26) | ||||
| 
 | ||||
| project(libfont CXX) | ||||
| 
 | ||||
| set(LIBGUI_SOURCES | ||||
| 	Font.cpp | ||||
| ) | ||||
| 
 | ||||
| add_custom_target(libfont-headers | ||||
| 	COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/ | ||||
| 	DEPENDS sysroot | ||||
| ) | ||||
| 
 | ||||
| add_library(libfont ${LIBGUI_SOURCES}) | ||||
| add_dependencies(libfont headers libc-install) | ||||
| target_link_libraries(libfont PUBLIC libc) | ||||
| 
 | ||||
| add_custom_target(libfont-install | ||||
| 	COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/libfont.a ${BANAN_LIB}/ | ||||
| 	DEPENDS libfont | ||||
| 	BYPRODUCTS ${BANAN_LIB}/libfont.a | ||||
| ) | ||||
| 
 | ||||
| set(CMAKE_STATIC_LIBRARY_PREFIX "") | ||||
|  | @ -1,9 +1,13 @@ | |||
| #include <BAN/Debug.h> | ||||
| #include <BAN/Endianness.h> | ||||
| #include <BAN/ScopeGuard.h> | ||||
| #include <BAN/UTF8.h> | ||||
| #include <kernel/Font.h> | ||||
| 
 | ||||
| #include <LibFont/Font.h> | ||||
| 
 | ||||
| #if __is_kernel | ||||
| #include <kernel/FS/VirtualFileSystem.h> | ||||
| #include <kernel/Process.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <fcntl.h> | ||||
| 
 | ||||
|  | @ -23,59 +27,86 @@ | |||
| #define PSF2_STARTSEQ			0xFE | ||||
| #define PSF2_SEPARATOR			0xFF | ||||
| 
 | ||||
| #if __is_kernel | ||||
| extern uint8_t _binary_font_prefs_psf_start[]; | ||||
| extern uint8_t _binary_font_prefs_psf_end[]; | ||||
| #endif | ||||
| 
 | ||||
| namespace Kernel | ||||
| namespace LibFont | ||||
| { | ||||
| 
 | ||||
| #if __is_kernel | ||||
| 	BAN::ErrorOr<Font> Font::prefs() | ||||
| 	{ | ||||
| 		size_t font_data_size = _binary_font_prefs_psf_end - _binary_font_prefs_psf_start; | ||||
| 		BAN::Span<const uint8_t> font_data(_binary_font_prefs_psf_start, font_data_size); | ||||
| 		return parse_psf1(font_data); | ||||
| 		return parse_psf1(BAN::ConstByteSpan(_binary_font_prefs_psf_start, font_data_size)); | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	BAN::ErrorOr<Font> Font::load(BAN::StringView path) | ||||
| 	{ | ||||
| 		auto inode = TRY(VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, path, O_RDONLY)).inode; | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> file_data; | ||||
| 		TRY(file_data.resize(inode->size())); | ||||
| 
 | ||||
| 		TRY(inode->read(0, file_data.span())); | ||||
| #if __is_kernel | ||||
| 		auto inode = TRY(Kernel::VirtualFileSystem::get().file_from_absolute_path({ 0, 0, 0, 0 }, path, O_RDONLY)).inode; | ||||
| 		TRY(file_data.resize(inode->size())); | ||||
| 		TRY(inode->read(0, BAN::ByteSpan(file_data.span()))); | ||||
| #else | ||||
| 		char path_buffer[PATH_MAX]; | ||||
| 		strncpy(path_buffer, path.data(), path.size()); | ||||
| 		path_buffer[path.size()] = '\0'; | ||||
| 
 | ||||
| 		int fd = open(path_buffer, O_RDONLY); | ||||
| 		if (fd == -1) | ||||
| 			return BAN::Error::from_errno(errno); | ||||
| 		BAN::ScopeGuard file_closer([fd] { close(fd); }); | ||||
| 
 | ||||
| 		struct stat st; | ||||
| 		if (fstat(fd, &st) == -1) | ||||
| 			return BAN::Error::from_errno(errno); | ||||
| 		TRY(file_data.resize(st.st_size)); | ||||
| 
 | ||||
| 		ssize_t total_read = 0; | ||||
| 		while (total_read < st.st_size) | ||||
| 		{ | ||||
| 			ssize_t nread = read(fd, file_data.data() + total_read, st.st_size - total_read); | ||||
| 			if (nread == -1) | ||||
| 				return BAN::Error::from_errno(errno); | ||||
| 			total_read += nread; | ||||
| 		} | ||||
| #endif | ||||
| 
 | ||||
| 		if (file_data.size() < 4) | ||||
| 			return BAN::Error::from_error_code(ErrorCode::Font_FileTooSmall); | ||||
| 			return BAN::Error::from_errno(EINVAL); | ||||
| 
 | ||||
| 		if (file_data[0] == PSF1_MAGIC0 && file_data[1] == PSF1_MAGIC1) | ||||
| 			return TRY(parse_psf1(file_data.span())); | ||||
| 			return TRY(parse_psf1(BAN::ConstByteSpan(file_data.span()))); | ||||
| 
 | ||||
| 		if (file_data[0] == PSF2_MAGIC0 && file_data[1] == PSF2_MAGIC1 && file_data[2] == PSF2_MAGIC2 && file_data[3] == PSF2_MAGIC3) | ||||
| 			return TRY(parse_psf2(file_data.span())); | ||||
| 			return TRY(parse_psf2(BAN::ConstByteSpan(file_data.span()))); | ||||
| 
 | ||||
| 		return BAN::Error::from_error_code(ErrorCode::Font_Unsupported); | ||||
| 		return BAN::Error::from_errno(ENOTSUP); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<Font> Font::parse_psf1(BAN::Span<const uint8_t> font_data) | ||||
| 	BAN::ErrorOr<Font> Font::parse_psf1(BAN::ConstByteSpan font_data) | ||||
| 	{ | ||||
| 		if (font_data.size() < 4) | ||||
| 			return BAN::Error::from_error_code(ErrorCode::Font_FileTooSmall); | ||||
| 
 | ||||
| 		struct PSF1Header | ||||
| 		{ | ||||
| 			uint8_t magic[2]; | ||||
| 			uint8_t mode; | ||||
| 			uint8_t char_size; | ||||
| 		}; | ||||
| 		const PSF1Header& header = *(const PSF1Header*)font_data.data(); | ||||
| 
 | ||||
| 		if (font_data.size() < sizeof(PSF1Header)) | ||||
| 			return BAN::Error::from_errno(EINVAL); | ||||
| 		const auto& header = font_data.as<const PSF1Header>(); | ||||
| 
 | ||||
| 		uint32_t glyph_count = header.mode & PSF1_MODE512 ? 512 : 256; | ||||
| 		uint32_t glyph_size = header.char_size; | ||||
| 		uint32_t glyph_data_size = glyph_size * glyph_count; | ||||
| 
 | ||||
| 		if (font_data.size() < sizeof(PSF1Header) + glyph_data_size) | ||||
| 			return BAN::Error::from_error_code(ErrorCode::Font_FileTooSmall); | ||||
| 			return BAN::Error::from_errno(EINVAL); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> glyph_data; | ||||
| 		TRY(glyph_data.resize(glyph_data_size)); | ||||
|  | @ -125,7 +156,7 @@ namespace Kernel | |||
| 		} | ||||
| 
 | ||||
| 		if (codepoint_redef) | ||||
| 			dwarnln("Font contsins multiple definitions for same codepoint(s)"); | ||||
| 			dwarnln("Font contains multiple definitions for same codepoint(s)"); | ||||
| 		if (codepoint_sequence) | ||||
| 			dwarnln("Font contains codepoint sequences (not supported)"); | ||||
| 
 | ||||
|  | @ -138,7 +169,7 @@ namespace Kernel | |||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<Font> Font::parse_psf2(BAN::Span<const uint8_t> font_data) | ||||
| 	BAN::ErrorOr<Font> Font::parse_psf2(BAN::ConstByteSpan font_data) | ||||
| 	{ | ||||
| 		struct PSF2Header | ||||
| 		{ | ||||
|  | @ -153,14 +184,13 @@ namespace Kernel | |||
| 		}; | ||||
| 
 | ||||
| 		if (font_data.size() < sizeof(PSF2Header)) | ||||
| 			return BAN::Error::from_error_code(ErrorCode::Font_FileTooSmall); | ||||
| 
 | ||||
| 		const PSF2Header& header = *(const PSF2Header*)font_data.data(); | ||||
| 			return BAN::Error::from_errno(EINVAL); | ||||
| 		const auto& header = font_data.as<const PSF2Header>(); | ||||
| 
 | ||||
| 		uint32_t glyph_data_size = header.glyph_count * header.glyph_size; | ||||
| 
 | ||||
| 		if (font_data.size() < glyph_data_size + header.header_size) | ||||
| 			return BAN::Error::from_error_code(ErrorCode::Font_FileTooSmall); | ||||
| 			return BAN::Error::from_errno(EINVAL); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> glyph_data; | ||||
| 		TRY(glyph_data.resize(glyph_data_size)); | ||||
|  | @ -244,14 +274,4 @@ namespace Kernel | |||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	bool Font::has_glyph(uint32_t codepoint) const | ||||
| 	{ | ||||
| 		return m_glyph_offsets.contains(codepoint); | ||||
| 	} | ||||
| 
 | ||||
| 	const uint8_t* Font::glyph(uint32_t codepoint) const | ||||
| 	{ | ||||
| 		return m_glyph_data.data() + m_glyph_offsets[codepoint]; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,43 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <BAN/ByteSpan.h> | ||||
| #include <BAN/HashMap.h> | ||||
| #include <BAN/StringView.h> | ||||
| 
 | ||||
| namespace LibFont | ||||
| { | ||||
| 
 | ||||
| 	class Font | ||||
| 	{ | ||||
| 	public: | ||||
| 		static BAN::ErrorOr<Font> load(BAN::StringView path); | ||||
| #if __is_kernel | ||||
| 		static BAN::ErrorOr<Font> prefs(); | ||||
| #endif | ||||
| 
 | ||||
| 		uint32_t width() const { return m_width; } | ||||
| 		uint32_t height() const { return m_height; } | ||||
| 		uint32_t pitch() const { return m_pitch; } | ||||
| 
 | ||||
| 		bool has_glyph(uint32_t codepoint) const { return glyph(codepoint) != nullptr; } | ||||
| 		const uint8_t* glyph(uint32_t codepoint) const | ||||
| 		{ | ||||
| 			auto it = m_glyph_offsets.find(codepoint); | ||||
| 			if (it == m_glyph_offsets.end()) | ||||
| 				return nullptr; | ||||
| 			return m_glyph_data.data() + it->value; | ||||
| 		} | ||||
| 
 | ||||
| 	private: | ||||
| 		static BAN::ErrorOr<Font> parse_psf1(BAN::ConstByteSpan); | ||||
| 		static BAN::ErrorOr<Font> parse_psf2(BAN::ConstByteSpan); | ||||
| 
 | ||||
| 	private: | ||||
| 		BAN::HashMap<uint32_t, uint32_t> m_glyph_offsets; | ||||
| 		BAN::Vector<uint8_t> m_glyph_data; | ||||
| 		uint32_t m_width = 0; | ||||
| 		uint32_t m_height = 0; | ||||
| 		uint32_t m_pitch = 0; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  | @ -13,7 +13,7 @@ add_custom_target(libgui-headers | |||
| 
 | ||||
| add_library(libgui ${LIBGUI_SOURCES}) | ||||
| add_dependencies(libgui headers libc-install) | ||||
| target_link_libraries(libgui PUBLIC libc) | ||||
| target_link_libraries(libgui PUBLIC libc libfont) | ||||
| 
 | ||||
| add_custom_target(libgui-install | ||||
| 	COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/libgui.a ${BANAN_LIB}/ | ||||
|  |  | |||
|  | @ -1,6 +1,9 @@ | |||
| #include "LibGUI/Window.h" | ||||
| 
 | ||||
| #include <LibFont/Font.h> | ||||
| 
 | ||||
| #include <fcntl.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/banan-os.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/select.h> | ||||
|  | @ -17,24 +20,49 @@ namespace LibGUI | |||
| 		close(m_server_fd); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<BAN::UniqPtr<Window>> Window::create(uint32_t width, uint32_t height) | ||||
| 	BAN::ErrorOr<BAN::UniqPtr<Window>> Window::create(uint32_t width, uint32_t height, BAN::StringView title) | ||||
| 	{ | ||||
| 		if (title.size() >= sizeof(WindowCreatePacket::title)) | ||||
| 			return BAN::Error::from_errno(EINVAL); | ||||
| 
 | ||||
| 		int server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); | ||||
| 		if (server_fd == -1) | ||||
| 			return BAN::Error::from_errno(errno); | ||||
| 
 | ||||
| 		if (fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL) | O_CLOEXEC) == -1) | ||||
| 			return BAN::Error::from_errno(errno); | ||||
| 
 | ||||
| 		timespec start_time; | ||||
| 		clock_gettime(CLOCK_MONOTONIC, &start_time); | ||||
| 
 | ||||
| 		for (;;) | ||||
| 		{ | ||||
| 			sockaddr_un server_address; | ||||
| 			server_address.sun_family = AF_UNIX; | ||||
| 			strcpy(server_address.sun_path, s_window_server_socket.data()); | ||||
| 		if (connect(server_fd, (sockaddr*)&server_address, sizeof(server_address)) == -1) | ||||
| 			if (connect(server_fd, (sockaddr*)&server_address, sizeof(server_address)) == 0) | ||||
| 				break; | ||||
| 
 | ||||
| 			timespec current_time; | ||||
| 			clock_gettime(CLOCK_MONOTONIC, ¤t_time); | ||||
| 			time_t duration_s = (current_time.tv_sec - start_time.tv_sec) + (current_time.tv_nsec >= start_time.tv_nsec); | ||||
| 			if (duration_s > 10) | ||||
| 			{ | ||||
| 				close(server_fd); | ||||
| 			return BAN::Error::from_errno(errno); | ||||
| 				return BAN::Error::from_errno(ETIMEDOUT); | ||||
| 			} | ||||
| 
 | ||||
| 			timespec sleep_time; | ||||
| 			sleep_time.tv_sec = 0; | ||||
| 			sleep_time.tv_nsec = 1'000'000; | ||||
| 			nanosleep(&sleep_time, nullptr); | ||||
| 		} | ||||
| 
 | ||||
| 		WindowCreatePacket packet; | ||||
| 		packet.width = width; | ||||
| 		packet.height = height; | ||||
| 		strncpy(packet.title, title.data(), title.size()); | ||||
| 		packet.title[title.size()] = '\0'; | ||||
| 		if (send(server_fd, &packet, sizeof(packet), 0) != sizeof(packet)) | ||||
| 		{ | ||||
| 			close(server_fd); | ||||
|  | @ -63,13 +91,92 @@ namespace LibGUI | |||
| 		)); | ||||
| 	} | ||||
| 
 | ||||
| 	bool Window::invalidate() | ||||
| 	void Window::fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color) | ||||
| 	{ | ||||
| 		if (!clamp_to_framebuffer(x, y, width, height)) | ||||
| 			return; | ||||
| 		for (uint32_t y_off = 0; y_off < height; y_off++) | ||||
| 			for (uint32_t x_off = 0; x_off < width; x_off++) | ||||
| 				set_pixel(x + x_off, y + y_off, color); | ||||
| 	} | ||||
| 
 | ||||
| 	void Window::draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color) | ||||
| 	{ | ||||
| 		if (tl_y + (int32_t)font.height() < 0 || tl_y >= (int32_t)height()) | ||||
| 			return; | ||||
| 		if (tl_x + (int32_t)font.width() < 0 || tl_x >= (int32_t)width()) | ||||
| 			return; | ||||
| 
 | ||||
| 		auto glyph = font.glyph(codepoint); | ||||
| 		if (glyph == nullptr) | ||||
| 			return; | ||||
| 
 | ||||
| 		for (int32_t off_y = 0; off_y < (int32_t)font.height(); off_y++) | ||||
| 		{ | ||||
| 			if (tl_y + off_y < 0) | ||||
| 				continue; | ||||
| 			uint32_t abs_y = tl_y + off_y; | ||||
| 			if (abs_y >= height()) | ||||
| 				break; | ||||
| 			for (int32_t off_x = 0; off_x < (int32_t)font.width(); off_x++) | ||||
| 			{ | ||||
| 				if (tl_x + off_x < 0) | ||||
| 					continue; | ||||
| 				uint32_t abs_x = tl_x + off_x; | ||||
| 				if (abs_x >= width()) | ||||
| 					break; | ||||
| 				const uint8_t bitmask = 1 << (font.width() - off_x - 1); | ||||
| 				if (glyph[off_y * font.pitch()] & bitmask) | ||||
| 					set_pixel(abs_x, abs_y, color); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	void Window::draw_text(BAN::StringView text, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color) | ||||
| 	{ | ||||
| 		for (size_t i = 0; i < text.size(); i++) | ||||
| 			draw_character(text[i], font, tl_x + (int32_t)(i * font.width()), tl_y, color); | ||||
| 	} | ||||
| 
 | ||||
| 	void Window::shift_vertical(int32_t amount) | ||||
| 	{ | ||||
| 		uint32_t amount_abs = BAN::Math::abs(amount); | ||||
| 		if (amount_abs == 0 || amount_abs >= height()) | ||||
| 			return; | ||||
| 		uint32_t* dst = (amount > 0) ? m_framebuffer + width() * amount_abs : m_framebuffer; | ||||
| 		uint32_t* src = (amount < 0) ? m_framebuffer + width() * amount_abs : m_framebuffer; | ||||
| 		memmove(dst, src, width() * (height() - amount_abs) * 4); | ||||
| 	} | ||||
| 
 | ||||
| 	bool Window::clamp_to_framebuffer(int32_t& signed_x, int32_t& signed_y, uint32_t& width, uint32_t& height) const | ||||
| 	{ | ||||
| 		int32_t min_x = BAN::Math::max<int32_t>(signed_x, 0); | ||||
| 		int32_t min_y = BAN::Math::max<int32_t>(signed_y, 0); | ||||
| 		int32_t max_x = BAN::Math::min<int32_t>(this->width(), signed_x + (int32_t)width); | ||||
| 		int32_t max_y = BAN::Math::min<int32_t>(this->height(), signed_y + (int32_t)height); | ||||
| 
 | ||||
| 		if (min_x >= max_x) | ||||
| 			return false; | ||||
| 		if (min_y >= max_y) | ||||
| 			return false; | ||||
| 
 | ||||
| 		signed_x = min_x; | ||||
| 		signed_y = min_y; | ||||
| 		width = max_x - min_x; | ||||
| 		height = max_y - min_y; | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	bool Window::invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height) | ||||
| 	{ | ||||
| 		if (!clamp_to_framebuffer(x, y, width, height)) | ||||
| 			return true; | ||||
| 
 | ||||
| 		WindowInvalidatePacket packet; | ||||
| 		packet.x = 0; | ||||
| 		packet.y = 0; | ||||
| 		packet.width = m_width; | ||||
| 		packet.height = m_height; | ||||
| 		packet.x = x; | ||||
| 		packet.y = y; | ||||
| 		packet.width = width; | ||||
| 		packet.height = height; | ||||
| 		return send(m_server_fd, &packet, sizeof(packet), 0) == sizeof(packet); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -92,6 +199,14 @@ namespace LibGUI | |||
| 
 | ||||
| 			switch (packet.type) | ||||
| 			{ | ||||
| 				case EventPacket::Type::DestroyWindow: | ||||
| 					exit(1); | ||||
| 				case EventPacket::Type::CloseWindow: | ||||
| 					if (m_close_window_event_callback) | ||||
| 						m_close_window_event_callback(); | ||||
| 					else | ||||
| 						exit(0); | ||||
| 					break; | ||||
| 				case EventPacket::Type::KeyEvent: | ||||
| 					if (m_key_event_callback) | ||||
| 						m_key_event_callback(packet.key_event); | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ | |||
| #include <limits.h> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| namespace LibFont { class Font; } | ||||
| 
 | ||||
| namespace LibGUI | ||||
| { | ||||
| 
 | ||||
|  | @ -20,6 +22,7 @@ namespace LibGUI | |||
| 		INVALID, | ||||
| 		CreateWindow, | ||||
| 		Invalidate, | ||||
| 		COUNT | ||||
| 	}; | ||||
| 
 | ||||
| 	struct WindowCreatePacket | ||||
|  | @ -27,6 +30,7 @@ namespace LibGUI | |||
| 		WindowPacketType type = WindowPacketType::CreateWindow; | ||||
| 		uint32_t width; | ||||
| 		uint32_t height; | ||||
| 		char title[52]; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct WindowInvalidatePacket | ||||
|  | @ -61,6 +65,8 @@ namespace LibGUI | |||
| 	{ | ||||
| 		enum class Type : uint8_t | ||||
| 		{ | ||||
| 			DestroyWindow, | ||||
| 			CloseWindow, | ||||
| 			KeyEvent, | ||||
| 			MouseButtonEvent, | ||||
| 			MouseMoveEvent, | ||||
|  | @ -97,7 +103,7 @@ namespace LibGUI | |||
| 	public: | ||||
| 		~Window(); | ||||
| 
 | ||||
| 		static BAN::ErrorOr<BAN::UniqPtr<Window>> create(uint32_t width, uint32_t height); | ||||
| 		static BAN::ErrorOr<BAN::UniqPtr<Window>> create(uint32_t width, uint32_t height, BAN::StringView title); | ||||
| 
 | ||||
| 		void set_pixel(uint32_t x, uint32_t y, uint32_t color) | ||||
| 		{ | ||||
|  | @ -106,17 +112,29 @@ namespace LibGUI | |||
| 			m_framebuffer[y * m_width + x] = color; | ||||
| 		} | ||||
| 
 | ||||
| 		bool invalidate(); | ||||
| 		void fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color); | ||||
| 		void fill(uint32_t color) { return fill_rect(0, 0, width(), height(), color); } | ||||
| 
 | ||||
| 		void draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color); | ||||
| 		void draw_text(BAN::StringView text, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color); | ||||
| 
 | ||||
| 		void shift_vertical(int32_t amount); | ||||
| 
 | ||||
| 		bool invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height); | ||||
| 		bool invalidate() { return invalidate(0, 0, width(), height()); } | ||||
| 
 | ||||
| 		uint32_t width() const { return m_width; } | ||||
| 		uint32_t height() const { return m_height; } | ||||
| 
 | ||||
| 		void poll_events(); | ||||
| 		void set_close_window_event_callback(BAN::Function<void()> callback)								{ m_close_window_event_callback = callback; } | ||||
| 		void set_key_event_callback(BAN::Function<void(EventPacket::KeyEvent)> callback)					{ m_key_event_callback = callback; } | ||||
| 		void set_mouse_button_event_callback(BAN::Function<void(EventPacket::MouseButtonEvent)> callback)	{ m_mouse_button_event_callback = callback; } | ||||
| 		void set_mouse_move_event_callback(BAN::Function<void(EventPacket::MouseMoveEvent)> callback)		{ m_mouse_move_event_callback = callback; } | ||||
| 		void set_mouse_scroll_event_callback(BAN::Function<void(EventPacket::MouseScrollEvent)> callback)	{ m_mouse_scroll_event_callback = callback; } | ||||
| 
 | ||||
| 		int server_fd() const { return m_server_fd; } | ||||
| 
 | ||||
| 	private: | ||||
| 		Window(int server_fd, uint32_t* framebuffer, uint32_t width, uint32_t height) | ||||
| 			: m_server_fd(server_fd) | ||||
|  | @ -125,12 +143,15 @@ namespace LibGUI | |||
| 			, m_height(height) | ||||
| 		{ } | ||||
| 
 | ||||
| 		bool clamp_to_framebuffer(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const; | ||||
| 
 | ||||
| 	private: | ||||
| 		int m_server_fd; | ||||
| 		uint32_t* m_framebuffer; | ||||
| 		uint32_t m_width; | ||||
| 		uint32_t m_height; | ||||
| 
 | ||||
| 		BAN::Function<void()>								m_close_window_event_callback; | ||||
| 		BAN::Function<void(EventPacket::KeyEvent)>			m_key_event_callback; | ||||
| 		BAN::Function<void(EventPacket::MouseButtonEvent)>	m_mouse_button_event_callback; | ||||
| 		BAN::Function<void(EventPacket::MouseMoveEvent)>	m_mouse_move_event_callback; | ||||
|  |  | |||
|  | @ -1,6 +1,9 @@ | |||
| #include <BAN/Array.h> | ||||
| #include <LibInput/KeyEvent.h> | ||||
| 
 | ||||
| #include <ctype.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| namespace LibInput | ||||
| { | ||||
| 
 | ||||
|  | @ -48,4 +51,71 @@ namespace LibInput | |||
| 		return (event.shift() ^ event.caps_lock()) ? utf8_upper[static_cast<uint8_t>(key)] : utf8_lower[static_cast<uint8_t>(key)]; | ||||
| 	} | ||||
| 
 | ||||
| 	const char* key_to_utf8_ansi(Key key, uint16_t modifier) | ||||
| 	{ | ||||
| 		static constexpr const char* utf8_lower[] = { | ||||
| 			nullptr, nullptr, | ||||
| 			"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", | ||||
| 			"å", "ä", "ö", | ||||
| 			"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", | ||||
| 			nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 			/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "\n", " ", | ||||
| 			"!", "\"", "#", "¤", "%", "&", "/", "§", "½", | ||||
| 			"(", ")", "[", "]", "{", "}", | ||||
| 			"=", "?", "+", "\\", "´", "`", "¨", "¸", "\b \b", "@", "£", "$", "€", | ||||
| 			nullptr, "\t", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 			"'", "*", "^", "~", "\e[A", "\e[B", "\e[D", "\e[C", | ||||
| 			",", ";", ".", ":", "-", "_", nullptr, nullptr, "<", ">", "|", "¬", "¦", | ||||
| 			"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", | ||||
| 			"+", "-", "*", "/", "\n", ",", | ||||
| 			nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 		}; | ||||
| 		static_assert((size_t)Key::Count == sizeof(utf8_lower) / sizeof(*utf8_lower)); | ||||
| 
 | ||||
| 		static constexpr const char* utf8_upper[] = { | ||||
| 			nullptr, nullptr, | ||||
| 			"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", | ||||
| 			"Å", "Ä", "Ö", | ||||
| 			"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", | ||||
| 			nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 			/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "\n", " ", | ||||
| 			"!", "\"", "#", "¤", "%", "&", "/", "§", "½", | ||||
| 			"(", ")", "[", "]", "{", "}", | ||||
| 			"=", "?", "+", "\\", "´", "`", "¨", "¸", "\b \b", "@", "£", "$", "€", | ||||
| 			nullptr, "\t", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 			"'", "*", "^", "~", "\e[A", "\e[B", "\e[D", "\e[C", | ||||
| 			",", ";", ".", ":", "-", "_", nullptr, nullptr, "<", ">", "|", "¬", "¦", | ||||
| 			"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", | ||||
| 			"+", "-", "*", "/", "\n", ",", | ||||
| 			nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 		}; | ||||
| 		static_assert((size_t)Key::Count == sizeof(utf8_upper) / sizeof(*utf8_upper)); | ||||
| 
 | ||||
| 		static constexpr const char* utf8_ctrl[] = { | ||||
| 			nullptr, nullptr, | ||||
| 			"\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x0A", "\x0B", "\x0C", "\x0D", "\x0E", "\x0F", "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1A", | ||||
| 			"Å", "Ä", "Ö", | ||||
| 			"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", | ||||
| 			nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 			/*"Insert", "PrintScreen", "Delete", "Home", "End", "PageUp", "PageDown",*/ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "\n", " ", | ||||
| 			"!", "\"", "#", "¤", "%", "&", "/", "§", "½", | ||||
| 			"(", ")", "[", "]", "{", "}", | ||||
| 			"=", "?", "+", "\\", "´", "`", "¨", "¸", "\b \b", "@", "£", "$", "€", | ||||
| 			nullptr, "\t", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 			"'", "*", "^", "~", "\e[A", "\e[B", "\e[D", "\e[C", | ||||
| 			",", ";", ".", ":", "-", "_", nullptr, nullptr, "<", ">", "|", "¬", "¦", | ||||
| 			"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", | ||||
| 			"+", "-", "*", "/", "\n", ",", | ||||
| 			nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | ||||
| 		}; | ||||
| 		static_assert((size_t)Key::Count == sizeof(utf8_upper) / sizeof(*utf8_upper)); | ||||
| 
 | ||||
| 		KeyEvent event { .modifier = modifier, .key = key }; | ||||
| 		if (event.ctrl()) | ||||
| 			return utf8_ctrl[static_cast<uint8_t>(key)]; | ||||
| 		if (event.shift() ^ event.caps_lock()) | ||||
| 			return utf8_upper[static_cast<uint8_t>(key)]; | ||||
| 		return utf8_lower[static_cast<uint8_t>(key)]; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -101,5 +101,6 @@ namespace LibInput | |||
| 	}; | ||||
| 
 | ||||
| 	const char* key_to_utf8(Key key, uint16_t modifier); | ||||
| 	const char* key_to_utf8_ansi(Key key, uint16_t modifier); | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -29,7 +29,6 @@ set(KERNEL_SOURCES | |||
| 	kernel/Device/NullDevice.cpp | ||||
| 	kernel/Device/ZeroDevice.cpp | ||||
| 	kernel/Errors.cpp | ||||
| 	kernel/Font.cpp | ||||
| 	kernel/FS/DevFS/FileSystem.cpp | ||||
| 	kernel/FS/Ext2/FileSystem.cpp | ||||
| 	kernel/FS/Ext2/Inode.cpp | ||||
|  | @ -149,6 +148,10 @@ set(LIBELF_SOURCES | |||
| 	../LibELF/LibELF/LoadableELF.cpp | ||||
| ) | ||||
| 
 | ||||
| set(LIBFONT_SOURCES | ||||
| 	../LibFont/Font.cpp | ||||
| ) | ||||
| 
 | ||||
| set(LIBINPUT_SOURCE | ||||
| 	../LibInput/KeyboardLayout.cpp | ||||
| 	../LibInput/KeyEvent.cpp | ||||
|  | @ -160,6 +163,7 @@ set(KERNEL_SOURCES | |||
| 	${BAN_SOURCES} | ||||
| 	${KLIBC_SOURCES} | ||||
| 	${LIBELF_SOURCES} | ||||
| 	${LIBFONT_SOURCES} | ||||
| 	${LIBINPUT_SOURCE} | ||||
| ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,35 +0,0 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <BAN/HashMap.h> | ||||
| #include <BAN/Span.h> | ||||
| #include <BAN/StringView.h> | ||||
| 
 | ||||
| namespace Kernel | ||||
| { | ||||
| 
 | ||||
| 	class Font | ||||
| 	{ | ||||
| 	public: | ||||
| 		static BAN::ErrorOr<Font> load(BAN::StringView); | ||||
| 		static BAN::ErrorOr<Font> prefs(); | ||||
| 
 | ||||
| 		uint32_t width() const { return m_width; } | ||||
| 		uint32_t height() const { return m_height; } | ||||
| 		uint32_t pitch() const { return m_pitch; } | ||||
| 
 | ||||
| 		bool has_glyph(uint32_t) const; | ||||
| 		const uint8_t* glyph(uint32_t) const; | ||||
| 
 | ||||
| 	private: | ||||
| 		static BAN::ErrorOr<Font> parse_psf1(BAN::Span<const uint8_t>); | ||||
| 		static BAN::ErrorOr<Font> parse_psf2(BAN::Span<const uint8_t>); | ||||
| 
 | ||||
| 	private: | ||||
| 		BAN::HashMap<uint32_t, uint32_t> m_glyph_offsets; | ||||
| 		BAN::Vector<uint8_t> m_glyph_data; | ||||
| 		uint32_t m_width = 0; | ||||
| 		uint32_t m_height = 0; | ||||
| 		uint32_t m_pitch = 0; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  | @ -21,6 +21,7 @@ namespace Kernel | |||
| 		static SharedMemoryObjectManager& get(); | ||||
| 
 | ||||
| 		BAN::ErrorOr<Key> create_object(size_t size, PageTable::flags_t); | ||||
| 		BAN::ErrorOr<void> delete_object(Key); | ||||
| 		BAN::ErrorOr<BAN::UniqPtr<SharedMemoryObject>> map_object(Key, PageTable&, AddressRange); | ||||
| 
 | ||||
| 	private: | ||||
|  | @ -29,6 +30,9 @@ namespace Kernel | |||
| 	private: | ||||
| 		struct Object : public BAN::RefCounted<Object> | ||||
| 		{ | ||||
| 			~Object(); | ||||
| 
 | ||||
| 			Key key; | ||||
| 			size_t size; | ||||
| 			PageTable::flags_t flags; | ||||
| 			BAN::Vector<paddr_t> paddrs; | ||||
|  | @ -51,7 +55,7 @@ namespace Kernel | |||
| 	public: | ||||
| 		static BAN::ErrorOr<BAN::UniqPtr<SharedMemoryObject>> create(BAN::RefPtr<SharedMemoryObjectManager::Object>, PageTable&, AddressRange); | ||||
| 
 | ||||
| 		virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override { return BAN::Error::from_errno(ENOTSUP); } | ||||
| 		virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override; | ||||
| 		virtual BAN::ErrorOr<void> msync(vaddr_t, size_t, int) override { return {}; } | ||||
| 
 | ||||
| 	protected: | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ namespace Kernel | |||
| 
 | ||||
| 	private: | ||||
| 		UnixDomainSocket(SocketType, ino_t, const TmpInodeInfo&); | ||||
| 		~UnixDomainSocket() { on_close_impl(); } | ||||
| 
 | ||||
| 		BAN::ErrorOr<void> add_packet(BAN::ConstByteSpan); | ||||
| 
 | ||||
|  | @ -46,6 +47,7 @@ namespace Kernel | |||
| 		{ | ||||
| 			bool										listening { false }; | ||||
| 			BAN::Atomic<bool>							connection_done { false }; | ||||
| 			mutable BAN::Atomic<bool>					target_closed { false }; | ||||
| 			BAN::WeakPtr<UnixDomainSocket>				connection; | ||||
| 			BAN::Queue<BAN::RefPtr<UnixDomainSocket>>	pending_connections; | ||||
| 			Semaphore									pending_semaphore; | ||||
|  |  | |||
|  | @ -159,8 +159,11 @@ namespace Kernel | |||
| 		BAN::ErrorOr<long> sys_msync(void* addr, size_t len, int flags); | ||||
| 
 | ||||
| 		BAN::ErrorOr<long> sys_smo_create(size_t len, int prot); | ||||
| 		BAN::ErrorOr<long> sys_smo_delete(SharedMemoryObjectManager::Key); | ||||
| 		BAN::ErrorOr<long> sys_smo_map(SharedMemoryObjectManager::Key); | ||||
| 
 | ||||
| 		BAN::ErrorOr<long> sys_isatty(int fildes); | ||||
| 
 | ||||
| 		BAN::ErrorOr<long> sys_tty_ctrl(int fildes, int command, int flags); | ||||
| 
 | ||||
| 		BAN::ErrorOr<long> sys_signal(int, void (*)(int)); | ||||
|  | @ -217,6 +220,11 @@ namespace Kernel | |||
| 			ASSERT(signal >= _SIGMIN); | ||||
| 			ASSERT(signal <= _SIGMAX); | ||||
| 			ASSERT(signal < 64); | ||||
| 			vaddr_t handler = m_signal_handlers[signal]; | ||||
| 			if (handler == (vaddr_t)SIG_IGN) | ||||
| 				return; | ||||
| 			if (handler == (vaddr_t)SIG_DFL && (signal == SIGCHLD || signal == SIGURG)) | ||||
| 				return; | ||||
| 			if (signal < 32) | ||||
| 				m_signal_pending_mask[0] |= (uint32_t)1 << signal; | ||||
| 			else | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ namespace Kernel | |||
| 	public: | ||||
| 		void set_termios(const termios& termios) { m_termios = termios; } | ||||
| 		termios get_termios() const { return m_termios; } | ||||
| 		virtual void set_font(const Font&) {}; | ||||
| 		virtual void set_font(const LibFont::Font&) {}; | ||||
| 
 | ||||
| 		void set_foreground_pgrp(pid_t pgrp) { m_foreground_pgrp = pgrp; } | ||||
| 		pid_t foreground_pgrp() const { return m_foreground_pgrp; } | ||||
|  |  | |||
|  | @ -1,12 +1,15 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <kernel/Font.h> | ||||
| #include <LibFont/Font.h> | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| class TerminalDriver | ||||
| namespace Kernel | ||||
| { | ||||
| public: | ||||
| 
 | ||||
| 	class TerminalDriver | ||||
| 	{ | ||||
| 	public: | ||||
| 		struct Color | ||||
| 		{ | ||||
| 			constexpr Color(uint32_t rgb) | ||||
|  | @ -21,8 +24,8 @@ public: | |||
| 			uint32_t rgb; | ||||
| 		}; | ||||
| 
 | ||||
| public: | ||||
| 	TerminalDriver() : m_font(MUST(Kernel::Font::prefs())) {} | ||||
| 	public: | ||||
| 		TerminalDriver() : m_font(MUST(LibFont::Font::prefs())) {} | ||||
| 		virtual ~TerminalDriver() {} | ||||
| 		virtual uint32_t width() const = 0; | ||||
| 		virtual uint32_t height() const = 0; | ||||
|  | @ -32,15 +35,15 @@ public: | |||
| 
 | ||||
| 		virtual void set_cursor_position(uint32_t, uint32_t) = 0; | ||||
| 
 | ||||
| 	void set_font(const Kernel::Font& font) { m_font = font; }; | ||||
| 	const Kernel::Font& font() const { return m_font; } | ||||
| 		void set_font(const LibFont::Font& font) { m_font = font; }; | ||||
| 		const LibFont::Font& font() const { return m_font; } | ||||
| 
 | ||||
| private: | ||||
| 	Kernel::Font m_font; | ||||
| }; | ||||
| 	private: | ||||
| 		LibFont::Font m_font; | ||||
| 	}; | ||||
| 
 | ||||
| namespace TerminalColor | ||||
| { | ||||
| 	namespace TerminalColor | ||||
| 	{ | ||||
| 		static constexpr TerminalDriver::Color BLACK			= 0x000000; | ||||
| 		static constexpr TerminalDriver::Color BLUE				= 0x0000AA; | ||||
| 		static constexpr TerminalDriver::Color GREEN			= 0x00AA00; | ||||
|  | @ -58,4 +61,6 @@ namespace TerminalColor | |||
| 		static constexpr TerminalDriver::Color BRIGHT_MAGENTA	= 0xFF55FF; | ||||
| 		static constexpr TerminalDriver::Color BRIGHT_YELLOW	= 0xFFFF55; | ||||
| 		static constexpr TerminalDriver::Color BRIGHT_WHITE		= 0xFFFFFF; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ namespace Kernel | |||
| 	public: | ||||
| 		static BAN::ErrorOr<BAN::RefPtr<VirtualTTY>> create(TerminalDriver*); | ||||
| 
 | ||||
| 		virtual void set_font(const Font&) override; | ||||
| 		virtual void set_font(const LibFont::Font&) override; | ||||
| 
 | ||||
| 		virtual uint32_t height() const override { return m_height; } | ||||
| 		virtual uint32_t width() const override { return m_width; } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| 
 | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| extern TerminalDriver* g_terminal_driver; | ||||
| extern Kernel::TerminalDriver* g_terminal_driver; | ||||
| 
 | ||||
| namespace Debug | ||||
| { | ||||
|  | @ -68,6 +68,8 @@ namespace Debug | |||
| 
 | ||||
| 	void putchar(char ch) | ||||
| 	{ | ||||
| 		using namespace Kernel; | ||||
| 
 | ||||
| 		if (Kernel::Serial::has_devices()) | ||||
| 			return Kernel::Serial::putchar_any(ch); | ||||
| 		if (Kernel::TTY::is_initialized()) | ||||
|  |  | |||
|  | @ -21,6 +21,13 @@ namespace Kernel | |||
| 		return *s_instance; | ||||
| 	} | ||||
| 
 | ||||
| 	SharedMemoryObjectManager::Object::~Object() | ||||
| 	{ | ||||
| 		for (auto paddr : paddrs) | ||||
| 			if (paddr) | ||||
| 				Heap::get().release_page(paddr); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<SharedMemoryObjectManager::Key> SharedMemoryObjectManager::create_object(size_t size, PageTable::flags_t flags) | ||||
| 	{ | ||||
| 		ASSERT(size % PAGE_SIZE == 0); | ||||
|  | @ -38,11 +45,24 @@ namespace Kernel | |||
| 		Key key = generate_key(); | ||||
| 		while (m_objects.contains(key)) | ||||
| 			key = generate_key(); | ||||
| 		object->key = key; | ||||
| 
 | ||||
| 		TRY(m_objects.insert(key, object)); | ||||
| 		return key; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<void> SharedMemoryObjectManager::delete_object(Key key) | ||||
| 	{ | ||||
| 		LockGuard _(m_mutex); | ||||
| 
 | ||||
| 		auto it = m_objects.find(key); | ||||
| 		if (it == m_objects.end()) | ||||
| 			return BAN::Error::from_errno(ENOENT); | ||||
| 
 | ||||
| 		m_objects.remove(it); | ||||
| 		return {}; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<BAN::UniqPtr<SharedMemoryObject>> SharedMemoryObjectManager::map_object(Key key, PageTable& page_table, AddressRange address_range) | ||||
| 	{ | ||||
| 		LockGuard _(m_mutex); | ||||
|  | @ -61,6 +81,12 @@ namespace Kernel | |||
| 		return BAN::move(smo); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> SharedMemoryObject::clone(PageTable& new_page_table) | ||||
| 	{ | ||||
| 		auto region = TRY(SharedMemoryObjectManager::get().map_object(m_object->key, new_page_table, { .start = vaddr(), .end = vaddr() + size() })); | ||||
| 		return BAN::UniqPtr<MemoryRegion>(BAN::move(region)); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<bool> SharedMemoryObject::allocate_page_containing_impl(vaddr_t address) | ||||
| 	{ | ||||
| 		ASSERT(contains(address)); | ||||
|  |  | |||
|  | @ -55,7 +55,15 @@ namespace Kernel | |||
| 			auto it = s_bound_sockets.find(m_bound_path); | ||||
| 			if (it != s_bound_sockets.end()) | ||||
| 				s_bound_sockets.remove(it); | ||||
| 			m_bound_path.clear(); | ||||
| 		} | ||||
| 		if (m_info.has<ConnectionInfo>()) | ||||
| 		{ | ||||
| 			auto& connection_info = m_info.get<ConnectionInfo>(); | ||||
| 			if (auto connection = connection_info.connection.lock(); connection && connection->m_info.has<ConnectionInfo>()) | ||||
| 				connection->m_info.get<ConnectionInfo>().target_closed = true; | ||||
| 		} | ||||
| 		m_info.clear(); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> UnixDomainSocket::accept_impl(sockaddr* address, socklen_t* address_len) | ||||
|  | @ -256,6 +264,8 @@ namespace Kernel | |||
| 		if (m_info.has<ConnectionInfo>()) | ||||
| 		{ | ||||
| 			auto& connection_info = m_info.get<ConnectionInfo>(); | ||||
| 			if (connection_info.target_closed) | ||||
| 				return true; | ||||
| 			if (!connection_info.pending_connections.empty()) | ||||
| 				return true; | ||||
| 			if (!connection_info.connection) | ||||
|  | @ -338,6 +348,8 @@ namespace Kernel | |||
| 		if (m_info.has<ConnectionInfo>()) | ||||
| 		{ | ||||
| 			auto& connection_info = m_info.get<ConnectionInfo>(); | ||||
| 			if (connection_info.target_closed.compare_exchange(true, false)) | ||||
| 				return 0; | ||||
| 			if (!connection_info.connection) | ||||
| 				return BAN::Error::from_errno(ENOTCONN); | ||||
| 		} | ||||
|  |  | |||
|  | @ -195,9 +195,13 @@ namespace Kernel | |||
| 		{ | ||||
| 			SpinLockGuard _(s_process_lock); | ||||
| 			for (size_t i = 0; i < s_processes.size(); i++) | ||||
| 			{ | ||||
| 				if (m_parent && s_processes[i]->pid() == m_parent) | ||||
| 					s_processes[i]->add_pending_signal(SIGCHLD); | ||||
| 				if (s_processes[i] == this) | ||||
| 					s_processes.remove(i); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		ProcFileSystem::get().on_process_delete(*this); | ||||
| 
 | ||||
|  | @ -1418,6 +1422,12 @@ namespace Kernel | |||
| 		return TRY(SharedMemoryObjectManager::get().create_object(len, page_flags)); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> Process::sys_smo_delete(SharedMemoryObjectManager::Key key) | ||||
| 	{ | ||||
| 		TRY(SharedMemoryObjectManager::get().delete_object(key)); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> Process::sys_smo_map(SharedMemoryObjectManager::Key key) | ||||
| 	{ | ||||
| 		auto region = TRY(SharedMemoryObjectManager::get().map_object(key, page_table(), { .start = 0x400000, .end = KERNEL_OFFSET })); | ||||
|  | @ -1427,6 +1437,15 @@ namespace Kernel | |||
| 		return m_mapped_regions.back()->vaddr(); | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> Process::sys_isatty(int fildes) | ||||
| 	{ | ||||
| 		LockGuard _(m_process_lock); | ||||
| 		auto inode = TRY(m_open_file_descriptors.inode_of(fildes)); | ||||
| 		if (!inode->is_tty()) | ||||
| 			return BAN::Error::from_errno(ENOTTY); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	BAN::ErrorOr<long> Process::sys_tty_ctrl(int fildes, int command, int flags) | ||||
| 	{ | ||||
| 		LockGuard _(m_process_lock); | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ namespace Kernel | |||
| 		m_terminal_driver->clear(m_background); | ||||
| 	} | ||||
| 
 | ||||
| 	void VirtualTTY::set_font(const Kernel::Font& font) | ||||
| 	void VirtualTTY::set_font(const LibFont::Font& font) | ||||
| 	{ | ||||
| 		m_terminal_driver->set_font(font); | ||||
| 
 | ||||
|  |  | |||
|  | @ -279,8 +279,21 @@ namespace Kernel | |||
| 		auto& interrupt_stack = *reinterpret_cast<InterruptStack*>(kernel_stack_top() - sizeof(InterruptStack)); | ||||
| 		if (!GDT::is_user_segment(interrupt_stack.cs)) | ||||
| 			return false; | ||||
| 		uint64_t full_pending_mask = m_signal_pending_mask | process().signal_pending_mask();; | ||||
| 		return full_pending_mask & ~m_signal_block_mask; | ||||
| 
 | ||||
| 		uint64_t full_pending_mask = m_signal_pending_mask | process().signal_pending_mask(); | ||||
| 		uint64_t signals = full_pending_mask & ~m_signal_block_mask; | ||||
| 		for (uint8_t i = 0; i < _SIGMAX; i++) | ||||
| 		{ | ||||
| 			if (!(signals & ((uint64_t)1 << i))) | ||||
| 				continue; | ||||
| 			vaddr_t handler = m_process->m_signal_handlers[i]; | ||||
| 			if (handler == (vaddr_t)SIG_IGN) | ||||
| 				continue; | ||||
| 			if (handler == (vaddr_t)SIG_DFL && (i == SIGCHLD || i == SIGURG)) | ||||
| 				continue; | ||||
| 			return true; | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bool Thread::can_add_signal_to_execute() const | ||||
|  | @ -395,7 +408,14 @@ namespace Kernel | |||
| 	bool Thread::add_signal(int signal) | ||||
| 	{ | ||||
| 		SpinLockGuard _(m_signal_lock); | ||||
| 
 | ||||
| 		if (m_process) | ||||
| 		{ | ||||
| 			vaddr_t handler = m_process->m_signal_handlers[signal]; | ||||
| 			if (handler == (vaddr_t)SIG_IGN) | ||||
| 				return false; | ||||
| 			if (handler == (vaddr_t)SIG_DFL && (signal == SIGCHLD || signal == SIGURG)) | ||||
| 				return false; | ||||
| 		} | ||||
| 		uint64_t mask = 1ull << signal; | ||||
| 		if (!(m_signal_block_mask & mask)) | ||||
| 		{ | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ static void parse_command_line() | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| TerminalDriver* g_terminal_driver = nullptr; | ||||
| Kernel::TerminalDriver* g_terminal_driver = nullptr; | ||||
| 
 | ||||
| static void init2(void*); | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,7 +38,9 @@ int load_keymap(const char* path); | |||
| 
 | ||||
| // Create shared memory object and return its key or -1 on error
 | ||||
| long smo_create(size_t size, int prot); | ||||
| // Map shared memory object defined by its key and return address or null on error
 | ||||
| // Delete shared memory object such that it will be no longer accessible with smo_map(). Existing mappings are still valid
 | ||||
| int smo_delete(long key); | ||||
| // Map shared memory object defined by its key and return address or null on error. Mappings can be unmapped using munmap()
 | ||||
| void* smo_map(long key); | ||||
| 
 | ||||
| __END_DECLS | ||||
|  |  | |||
|  | @ -75,7 +75,9 @@ __BEGIN_DECLS | |||
| 	O(SYS_PSELECT,			pselect)		\ | ||||
| 	O(SYS_TRUNCATE,			truncate)		\ | ||||
| 	O(SYS_SMO_CREATE,		smo_create)		\ | ||||
| 	O(SYS_SMO_DELETE,		smo_delete)		\ | ||||
| 	O(SYS_SMO_MAP,			smo_map)		\ | ||||
| 	O(SYS_ISATTY,			isatty)			\ | ||||
| 
 | ||||
| enum Syscall | ||||
| { | ||||
|  |  | |||
|  | @ -22,6 +22,11 @@ long smo_create(size_t size, int prot) | |||
| 	return syscall(SYS_SMO_CREATE, size, prot); | ||||
| } | ||||
| 
 | ||||
| int smo_delete(long key) | ||||
| { | ||||
| 	return syscall(SYS_SMO_DELETE, key); | ||||
| } | ||||
| 
 | ||||
| void* smo_map(long key) | ||||
| { | ||||
| 	long ret = syscall(SYS_SMO_MAP, key); | ||||
|  |  | |||
|  | @ -114,6 +114,11 @@ int dup2(int fildes, int fildes2) | |||
| 	return syscall(SYS_DUP2, fildes, fildes2); | ||||
| } | ||||
| 
 | ||||
| int isatty(int fildes) | ||||
| { | ||||
| 	return syscall(SYS_ISATTY, fildes) >= 0; | ||||
| } | ||||
| 
 | ||||
| int execl(const char* pathname, const char* arg0, ...) | ||||
| { | ||||
| 	if (arg0 == nullptr) | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ set(USERSPACE_PROJECTS | |||
| 	sudo | ||||
| 	sync | ||||
| 	tee | ||||
| 	Terminal | ||||
| 	test | ||||
| 	test-framebuffer | ||||
| 	test-globals | ||||
|  |  | |||
|  | @ -331,6 +331,15 @@ BAN::Optional<int> execute_builtin(BAN::Vector<BAN::String>& args, int fd_in, in | |||
| 		while (*current) | ||||
| 			fprintf(fout, "%s\n", *current++); | ||||
| 	} | ||||
| 	else if (args.front() == "start-gui"sv) | ||||
| 	{ | ||||
| 		pid_t pid = fork(); | ||||
| 		if (pid == 0) | ||||
| 			execl("/bin/WindowServer", "WindowServer", NULL); | ||||
| 		if (fork() == 0) | ||||
| 			execl("/bin/Terminal", "Terminal", NULL); | ||||
| 		waitpid(pid, nullptr, 0); | ||||
| 	} | ||||
| 	else if (args.front() == "page-fault-test"sv) | ||||
| 	{ | ||||
| 		volatile int* ptr = nullptr; | ||||
|  | @ -601,7 +610,7 @@ pid_t execute_command_no_wait(BAN::Vector<BAN::String>& args, int fd_in, int fd_ | |||
| 				perror("setpgid"); | ||||
| 				exit(1); | ||||
| 			} | ||||
| 			if (tcsetpgrp(0, getpgrp()) == -1) | ||||
| 			if (isatty(0) && tcsetpgrp(0, getpgrp()) == -1) | ||||
| 			{ | ||||
| 				perror("tcsetpgrp"); | ||||
| 				exit(1); | ||||
|  | @ -633,7 +642,7 @@ int execute_command(BAN::Vector<BAN::String>& args, int fd_in, int fd_out) | |||
| 	if (waitpid(pid, &status, 0) == -1) | ||||
| 		ERROR_RETURN("waitpid", 1); | ||||
| 
 | ||||
| 	if (tcsetpgrp(0, getpgrp()) == -1) | ||||
| 	if (isatty(0) && tcsetpgrp(0, getpgrp()) == -1) | ||||
| 		ERROR_RETURN("tcsetpgrp", 1); | ||||
| 
 | ||||
| 	if (WIFSIGNALED(status)) | ||||
|  | @ -711,7 +720,7 @@ int execute_piped_commands(BAN::Vector<BAN::Vector<BAN::String>>& commands) | |||
| 			exit_codes[i] = WEXITSTATUS(status); | ||||
| 	} | ||||
| 
 | ||||
| 	if (tcsetpgrp(0, getpgrp()) == -1) | ||||
| 	if (isatty(0) && tcsetpgrp(0, getpgrp()) == -1) | ||||
| 		ERROR_RETURN("tcsetpgrp", 1); | ||||
| 
 | ||||
| 	return exit_codes.back(); | ||||
|  | @ -1053,7 +1062,7 @@ int main(int argc, char** argv) | |||
| 			buffers[index].clear(); | ||||
| 			col = 0; | ||||
| 			break; | ||||
| 		case '\x04': | ||||
| 		case '\x04': // ^D
 | ||||
| 			fprintf(stdout, "\n"); | ||||
| 			clean_exit(); | ||||
| 			break; | ||||
|  | @ -1075,6 +1084,9 @@ int main(int argc, char** argv) | |||
| 			break; | ||||
| 		default: | ||||
| 			MUST(buffers[index].insert(ch, col++)); | ||||
| 			if (col == buffers[index].size()) | ||||
| 				fputc(ch, stdout); | ||||
| 			else | ||||
| 				fprintf(stdout, "%c\e[s%s\e[u", ch, buffers[index].data() + col); | ||||
| 			fflush(stdout); | ||||
| 			break; | ||||
|  |  | |||
|  | @ -0,0 +1,17 @@ | |||
| cmake_minimum_required(VERSION 3.26) | ||||
| 
 | ||||
| project(Terminal CXX) | ||||
| 
 | ||||
| set(SOURCES | ||||
| 	main.cpp | ||||
| 	Terminal.cpp | ||||
| ) | ||||
| 
 | ||||
| add_executable(Terminal ${SOURCES}) | ||||
| target_compile_options(Terminal PUBLIC -O2 -g) | ||||
| target_link_libraries(Terminal PUBLIC libc ban libfont libgui libinput) | ||||
| 
 | ||||
| add_custom_target(Terminal-install | ||||
| 	COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/Terminal ${BANAN_BIN}/ | ||||
| 	DEPENDS Terminal | ||||
| ) | ||||
|  | @ -0,0 +1,380 @@ | |||
| #include "Terminal.h" | ||||
| 
 | ||||
| #include <BAN/Debug.h> | ||||
| #include <BAN/UTF8.h> | ||||
| 
 | ||||
| #include <ctype.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/select.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| void Terminal::start_shell() | ||||
| { | ||||
| 	int shell_stdin[2]; | ||||
| 	if (pipe(shell_stdin) == -1) | ||||
| 	{ | ||||
| 		dwarnln("pipe: {}", strerror(errno)); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	int shell_stdout[2]; | ||||
| 	if (pipe(shell_stdout) == -1) | ||||
| 	{ | ||||
| 		dwarnln("pipe: {}", strerror(errno)); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	int shell_stderr[2]; | ||||
| 	if (pipe(shell_stderr) == -1) | ||||
| 	{ | ||||
| 		dwarnln("pipe: {}", strerror(errno)); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	pid_t shell_pid = fork(); | ||||
| 	if (shell_pid == 0) | ||||
| 	{ | ||||
| 		if (dup2(shell_stdin[0], STDIN_FILENO) == -1) | ||||
| 		{ | ||||
| 			dwarnln("dup2: {}", strerror(errno)); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 		close(shell_stdin[0]); | ||||
| 		close(shell_stdin[1]); | ||||
| 
 | ||||
| 		if (dup2(shell_stdout[1], STDOUT_FILENO) == -1) | ||||
| 		{ | ||||
| 			dwarnln("dup2: {}", strerror(errno)); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 		close(shell_stdout[0]); | ||||
| 		close(shell_stdout[1]); | ||||
| 
 | ||||
| 		if (dup2(shell_stderr[1], STDERR_FILENO) == -1) | ||||
| 		{ | ||||
| 			dwarnln("dup2: {}", strerror(errno)); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 		close(shell_stderr[0]); | ||||
| 		close(shell_stderr[1]); | ||||
| 
 | ||||
| 		execl("/bin/Shell", "Shell", NULL); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	if (shell_pid == -1) | ||||
| 	{ | ||||
| 		dwarnln("fork: {}", strerror(errno)); | ||||
| 		exit(1); | ||||
| 	} | ||||
| 
 | ||||
| 	close(shell_stdin[0]); | ||||
| 	close(shell_stdout[1]); | ||||
| 	close(shell_stderr[1]); | ||||
| 
 | ||||
| 	m_shell_info = { | ||||
| 		.in = shell_stdin[1], | ||||
| 		.out = shell_stdout[0], | ||||
| 		.err = shell_stderr[0], | ||||
| 		.pid = shell_pid | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| static volatile bool s_shell_exited = false; | ||||
| 
 | ||||
| void Terminal::run() | ||||
| { | ||||
| 	signal(SIGCHLD, [](int) { s_shell_exited = true; }); | ||||
| 	start_shell(); | ||||
| 
 | ||||
| 	m_window = MUST(LibGUI::Window::create(600, 400, "Terminal"sv)); | ||||
| 	m_window->fill(m_bg_color); | ||||
| 	m_window->invalidate(); | ||||
| 
 | ||||
| 	m_font = MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"sv)); | ||||
| 
 | ||||
| 	m_window->set_key_event_callback([&](LibGUI::EventPacket::KeyEvent event) { on_key_event(event); }); | ||||
| 
 | ||||
| 	const int max_fd = BAN::Math::max(BAN::Math::max(m_shell_info.out, m_shell_info.err), m_window->server_fd()); | ||||
| 	while (!s_shell_exited) | ||||
| 	{ | ||||
| 		fd_set fds; | ||||
| 		FD_ZERO(&fds); | ||||
| 		FD_SET(m_shell_info.out, &fds); | ||||
| 		FD_SET(m_shell_info.err, &fds); | ||||
| 		FD_SET(m_window->server_fd(), &fds); | ||||
| 
 | ||||
| 		select(max_fd + 1, &fds, nullptr, nullptr, nullptr); | ||||
| 
 | ||||
| 		if (FD_ISSET(m_shell_info.out, &fds)) | ||||
| 			if (!read_shell(m_shell_info.out)) | ||||
| 				break; | ||||
| 		if (FD_ISSET(m_shell_info.err, &fds)) | ||||
| 			if (!read_shell(m_shell_info.err)) | ||||
| 				break; | ||||
| 		if (FD_ISSET(m_window->server_fd(), &fds)) | ||||
| 			m_window->poll_events(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool Terminal::read_shell(int fd) | ||||
| { | ||||
| 	char buffer[128]; | ||||
| 	ssize_t nread = read(fd, buffer, sizeof(buffer) - 1); | ||||
| 	if (nread < 0) | ||||
| 		dwarnln("read: {}", strerror(errno)); | ||||
| 	if (nread <= 0) | ||||
| 		return false; | ||||
| 	for (ssize_t i = 0; i < nread; i++) | ||||
| 		putchar(buffer[i]); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void Terminal::handle_sgr() | ||||
| { | ||||
| 	constexpr uint32_t colors_default[] { | ||||
| 		0x555555, | ||||
| 		0xFF5555, | ||||
| 		0x55FF55, | ||||
| 		0xFFFF55, | ||||
| 		0x5555FF, | ||||
| 		0xFF55FF, | ||||
| 		0x55FFFF, | ||||
| 		0xFFFFFF, | ||||
| 	}; | ||||
| 
 | ||||
| 	constexpr uint32_t colors_bright[] { | ||||
| 		0xAAAAAA, | ||||
| 		0xFFAAAA, | ||||
| 		0xAAFFAA, | ||||
| 		0xFFFFAA, | ||||
| 		0xAAAAFF, | ||||
| 		0xFFAAFF, | ||||
| 		0xAAFFFF, | ||||
| 		0xFFFFFF, | ||||
| 	}; | ||||
| 
 | ||||
| 	switch (m_csi_info.fields[0]) | ||||
| 	{ | ||||
| 		case -1: case 0: | ||||
| 			m_fg_color = 0xFFFFFF; | ||||
| 			m_bg_color = 0x000000; | ||||
| 			break; | ||||
| 		case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: | ||||
| 			m_fg_color = colors_default[m_csi_info.fields[0] - 30]; | ||||
| 			break; | ||||
| 		case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: | ||||
| 			m_bg_color = colors_default[m_csi_info.fields[0] - 40]; | ||||
| 			break; | ||||
| 		case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: | ||||
| 			m_fg_color = colors_bright[m_csi_info.fields[0] - 90]; | ||||
| 			break; | ||||
| 		case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: | ||||
| 			m_bg_color = colors_bright[m_csi_info.fields[0] - 100]; | ||||
| 			break; | ||||
| 		default: | ||||
| 			dprintln("TODO: SGR {}", m_csi_info.fields[0]); | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Terminal::handle_csi(char ch) | ||||
| { | ||||
| 	if (ch == ';') | ||||
| 	{ | ||||
| 		m_csi_info.index++; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ch == '?') | ||||
| 		return; | ||||
| 
 | ||||
| 	if (isdigit(ch)) | ||||
| 	{ | ||||
| 		if (m_csi_info.index <= 1) | ||||
| 		{ | ||||
| 			auto& field = m_csi_info.fields[m_csi_info.index]; | ||||
| 			field = (BAN::Math::max(field, 0) * 10) + (ch - '0'); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (ch) | ||||
| 	{ | ||||
| 		case 'C': | ||||
| 			if (m_csi_info.fields[0] == -1) | ||||
| 				m_csi_info.fields[0] = 1; | ||||
| 			m_cursor.x = BAN::Math::clamp<int32_t>(m_cursor.x + m_csi_info.fields[0], 0, cols() - 1); | ||||
| 			break; | ||||
| 		case 'D': | ||||
| 			if (m_csi_info.fields[0] == -1) | ||||
| 				m_csi_info.fields[0] = 1; | ||||
| 			m_cursor.x = BAN::Math::clamp<int32_t>((int32_t)m_cursor.x - m_csi_info.fields[0], 0, cols() - 1); | ||||
| 			break; | ||||
| 		case 'G': | ||||
| 			m_cursor.x = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, cols()) - 1; | ||||
| 			break; | ||||
| 		case 'H': | ||||
| 			m_cursor.y = BAN::Math::clamp<int32_t>(m_csi_info.fields[0], 1, rows()) - 1; | ||||
| 			m_cursor.x = BAN::Math::clamp<int32_t>(m_csi_info.fields[1], 1, cols()) - 1; | ||||
| 			break; | ||||
| 		case 'J': | ||||
| 		{ | ||||
| 			uint32_t rects[2][4] { { (uint32_t)-1 }, { (uint32_t)-1 } }; | ||||
| 
 | ||||
| 			if (m_csi_info.fields[0] == -1 || m_csi_info.fields[0] == 0) | ||||
| 			{ | ||||
| 				rects[0][0] = m_cursor.x * m_font.width(); | ||||
| 				rects[0][1] = m_cursor.y * m_font.height(); | ||||
| 				rects[0][2] = m_window->width() - rects[0][0]; | ||||
| 				rects[0][3] = m_font.height(); | ||||
| 
 | ||||
| 				rects[1][0] = 0; | ||||
| 				rects[1][1] = (m_cursor.y + 1) * m_font.height(); | ||||
| 				rects[1][2] = m_window->width(); | ||||
| 				rects[1][3] = m_window->height() - rects[1][1]; | ||||
| 			} | ||||
| 			else if (m_csi_info.fields[0] == 1) | ||||
| 			{ | ||||
| 				rects[0][0] = 0; | ||||
| 				rects[0][1] = m_cursor.y * m_font.height(); | ||||
| 				rects[0][2] = m_cursor.x * m_font.width(); | ||||
| 				rects[0][3] = m_font.height(); | ||||
| 
 | ||||
| 				rects[1][0] = 0; | ||||
| 				rects[1][1] = 0; | ||||
| 				rects[1][2] = m_window->width(); | ||||
| 				rects[1][3] = m_cursor.y * m_font.height(); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				rects[0][0] = 0; | ||||
| 				rects[0][1] = 0; | ||||
| 				rects[0][2] = m_window->width(); | ||||
| 				rects[0][3] = m_window->height(); | ||||
| 			} | ||||
| 
 | ||||
| 			for (int i = 0; i < 2; i++) | ||||
| 			{ | ||||
| 				if (rects[i][0] == (uint32_t)-1) | ||||
| 					continue; | ||||
| 				m_window->fill_rect(rects[i][0], rects[i][1], rects[i][2], rects[i][3], m_bg_color); | ||||
| 				m_window->invalidate(rects[i][0], rects[i][1], rects[i][2], rects[i][3]); | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'K': | ||||
| 		{ | ||||
| 			m_csi_info.fields[0] = BAN::Math::max(m_csi_info.fields[0], 0); | ||||
| 
 | ||||
| 			uint32_t rect[4]; | ||||
| 			rect[0] = (m_csi_info.fields[0] == 0) ? m_cursor.x * m_font.width() : 0; | ||||
| 			rect[1] = m_cursor.y * m_font.height(); | ||||
| 			rect[2] = (m_csi_info.fields[0] == 1) ? m_cursor.x * m_font.width() : m_window->width() - rect[0]; | ||||
| 			rect[3] = m_font.height(); | ||||
| 
 | ||||
| 			m_window->fill_rect(rect[0], rect[1], rect[2], rect[3], m_bg_color); | ||||
| 			m_window->invalidate(rect[0], rect[1], rect[2], rect[3]); | ||||
| 
 | ||||
| 			break; | ||||
| 		} | ||||
| 		case 'm': | ||||
| 			handle_sgr(); | ||||
| 			break; | ||||
| 		case 's': | ||||
| 			m_saved_cursor = m_cursor; | ||||
| 			break; | ||||
| 		case 'u': | ||||
| 			m_cursor = m_saved_cursor; | ||||
| 			break; | ||||
| 		default: | ||||
| 			dprintln("TODO: CSI {}", ch); | ||||
| 			break; | ||||
| 	} | ||||
| 	m_state = State::Normal; | ||||
| } | ||||
| 
 | ||||
| void Terminal::putchar(uint32_t codepoint) | ||||
| { | ||||
| 	if (m_state == State::ESC) | ||||
| 	{ | ||||
| 		if (codepoint != '[') | ||||
| 		{ | ||||
| 			dprintln("unknown escape character 0x{H}", codepoint); | ||||
| 			m_state = State::Normal; | ||||
| 			return; | ||||
| 		} | ||||
| 		m_state = State::CSI; | ||||
| 		m_csi_info.index = 0; | ||||
| 		m_csi_info.fields[0] = -1; | ||||
| 		m_csi_info.fields[1] = -1; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_state == State::CSI) | ||||
| 	{ | ||||
| 		if (codepoint < 0x20 || codepoint > 0xFE) | ||||
| 		{ | ||||
| 			dprintln("invalid CSI 0x{H}", codepoint); | ||||
| 			m_state = State::Normal; | ||||
| 			return; | ||||
| 		} | ||||
| 		handle_csi(codepoint); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (codepoint) | ||||
| 	{ | ||||
| 		case '\e': | ||||
| 			m_state = State::ESC; | ||||
| 			break; | ||||
| 		case '\n': | ||||
| 			m_cursor.x = 0; | ||||
| 			m_cursor.y++; | ||||
| 			break; | ||||
| 		case '\r': | ||||
| 			m_cursor.x = 0; | ||||
| 			break; | ||||
| 		case '\b': | ||||
| 			if (m_cursor.x > 0) | ||||
| 				m_cursor.x--; | ||||
| 			break; | ||||
| 		default: | ||||
| 		{ | ||||
| 			const uint32_t cell_w = m_font.width(); | ||||
| 			const uint32_t cell_h = m_font.height(); | ||||
| 			const uint32_t cell_x = m_cursor.x * cell_w; | ||||
| 			const uint32_t cell_y = m_cursor.y * cell_h; | ||||
| 
 | ||||
| 			m_window->fill_rect(cell_x, cell_y, cell_w, cell_h, m_bg_color); | ||||
| 			m_window->draw_character(codepoint, m_font, cell_x, cell_y, m_fg_color); | ||||
| 			m_window->invalidate(cell_x, cell_y, cell_w, cell_h); | ||||
| 			m_cursor.x++; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_cursor.x >= cols()) | ||||
| 	{ | ||||
| 		m_cursor.x = 0; | ||||
| 		m_cursor.y++; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_cursor.y >= rows()) | ||||
| 	{ | ||||
| 		uint32_t scroll = m_cursor.y - rows() + 1; | ||||
| 		m_cursor.y -= scroll; | ||||
| 		m_window->shift_vertical(-scroll * (int32_t)m_font.height()); | ||||
| 		m_window->fill_rect(0, m_window->height() - scroll * m_font.height(), m_window->width(), scroll * m_font.height(), m_bg_color); | ||||
| 		m_window->invalidate(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Terminal::on_key_event(LibGUI::EventPacket::KeyEvent event) | ||||
| { | ||||
| 	if (event.released()) | ||||
| 		return; | ||||
| 	if (const char* text = LibInput::key_to_utf8_ansi(event.key, event.modifier)) | ||||
| 		write(m_shell_info.in, text, strlen(text)); | ||||
| } | ||||
|  | @ -0,0 +1,64 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <LibFont/Font.h> | ||||
| 
 | ||||
| #include <LibGUI/Window.h> | ||||
| 
 | ||||
| class Terminal | ||||
| { | ||||
| public: | ||||
| 	void run(); | ||||
| 
 | ||||
| 	uint32_t cols() const { return m_window->width() / m_font.width(); } | ||||
| 	uint32_t rows() const { return m_window->height() / m_font.height(); } | ||||
| 
 | ||||
| private: | ||||
| 	void handle_csi(char ch); | ||||
| 	void handle_sgr(); | ||||
| 	void putchar(uint32_t codepoint); | ||||
| 	bool read_shell(int fd); | ||||
| 
 | ||||
| 	void on_key_event(LibGUI::EventPacket::KeyEvent); | ||||
| 
 | ||||
| 	void start_shell(); | ||||
| 
 | ||||
| private: | ||||
| 	struct Cursor | ||||
| 	{ | ||||
| 		uint32_t x; | ||||
| 		uint32_t y; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct ShellInfo | ||||
| 	{ | ||||
| 		int in; | ||||
| 		int out; | ||||
| 		int err; | ||||
| 		pid_t pid; | ||||
| 	}; | ||||
| 
 | ||||
| 	enum class State | ||||
| 	{ | ||||
| 		Normal, | ||||
| 		ESC, | ||||
| 		CSI, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct CSIInfo | ||||
| 	{ | ||||
| 		int32_t fields[2]; | ||||
| 		size_t index; | ||||
| 	}; | ||||
| 
 | ||||
| private: | ||||
| 	BAN::UniqPtr<LibGUI::Window> m_window; | ||||
| 	LibFont::Font m_font; | ||||
| 	Cursor m_cursor { 0, 0 }; | ||||
| 	ShellInfo m_shell_info; | ||||
| 	State m_state { State::Normal }; | ||||
| 	CSIInfo m_csi_info; | ||||
| 
 | ||||
| 	Cursor m_saved_cursor { 0, 0 }; | ||||
| 	uint32_t m_fg_color { 0xFFFFFF }; | ||||
| 	uint32_t m_bg_color { 0x000000 }; | ||||
| }; | ||||
|  | @ -0,0 +1,7 @@ | |||
| #include "Terminal.h" | ||||
| 
 | ||||
| int main() | ||||
| { | ||||
| 	Terminal terminal; | ||||
| 	terminal.run(); | ||||
| } | ||||
|  | @ -5,12 +5,13 @@ project(WindowServer CXX) | |||
| set(SOURCES | ||||
| 	main.cpp | ||||
| 	Framebuffer.cpp | ||||
| 	Window.cpp | ||||
| 	WindowServer.cpp | ||||
| ) | ||||
| 
 | ||||
| add_executable(WindowServer ${SOURCES}) | ||||
| target_compile_options(WindowServer PUBLIC -O2 -g) | ||||
| target_link_libraries(WindowServer PUBLIC libc ban libgui libinput) | ||||
| target_link_libraries(WindowServer PUBLIC libc ban libfont libgui libinput) | ||||
| 
 | ||||
| add_custom_target(WindowServer-install | ||||
| 	COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/WindowServer ${BANAN_BIN}/ | ||||
|  |  | |||
|  | @ -63,3 +63,18 @@ struct Rectangle | |||
| 	} | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| struct Circle | ||||
| { | ||||
| 	int32_t x; | ||||
| 	int32_t y; | ||||
| 	int32_t radius; | ||||
| 
 | ||||
| 	bool contains(Position position) const | ||||
| 	{ | ||||
| 		int32_t dx = position.x - x; | ||||
| 		int32_t dy = position.y - y; | ||||
| 		return dx * dx + dy * dy <= radius * radius; | ||||
| 	} | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -0,0 +1,72 @@ | |||
| #include "Window.h" | ||||
| 
 | ||||
| #include <BAN/Debug.h> | ||||
| 
 | ||||
| #include <LibGUI/Window.h> | ||||
| 
 | ||||
| #include <sys/banan-os.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/socket.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font) | ||||
| 	: m_client_fd(fd) | ||||
| 	, m_client_area(area) | ||||
| 	, m_smo_key(smo_key) | ||||
| { | ||||
| 	MUST(m_title.append(title)); | ||||
| 	prepare_title_bar(font); | ||||
| 
 | ||||
| 	m_fb_addr = static_cast<uint32_t*>(smo_map(smo_key)); | ||||
| 	ASSERT(m_fb_addr); | ||||
| 	memset(m_fb_addr, 0, client_width() * client_height() * 4); | ||||
| } | ||||
| 
 | ||||
| Window::~Window() | ||||
| { | ||||
| 	munmap(m_fb_addr, client_width() * client_height() * 4); | ||||
| 	smo_delete(m_smo_key); | ||||
| 
 | ||||
| 	LibGUI::EventPacket event; | ||||
| 	event.type = LibGUI::EventPacket::Type::DestroyWindow; | ||||
| 	send(m_client_fd, &event, sizeof(event), 0); | ||||
| 	close(m_client_fd); | ||||
| } | ||||
| 
 | ||||
| void Window::prepare_title_bar(const LibFont::Font& font) | ||||
| { | ||||
| 	const size_t title_bar_bytes = title_bar_width() * title_bar_height() * 4; | ||||
| 	uint32_t* title_bar_data = new uint32_t[title_bar_bytes]; | ||||
| 	ASSERT(title_bar_data); | ||||
| 	for (size_t i = 0; i < title_bar_bytes; i++) | ||||
| 		title_bar_data[i] = 0xFFFFFF; | ||||
| 
 | ||||
| 	const auto text_area = title_text_area(); | ||||
| 
 | ||||
| 	for (size_t i = 0; i < m_title.size() && (i + 1) * font.width() < static_cast<uint32_t>(text_area.width); i++) | ||||
| 	{ | ||||
| 		const auto* glyph = font.glyph(m_title[i]); | ||||
| 		if (glyph == nullptr) | ||||
| 			continue; | ||||
| 
 | ||||
| 		const int32_t y_off = (font.height() < (uint32_t)title_bar_height()) ? (title_bar_height() - font.height()) / 2 : 0; | ||||
| 		const int32_t x_off = y_off + i * font.width(); | ||||
| 		for (int32_t y = 0; (uint32_t)y < font.height(); y++) | ||||
| 		{ | ||||
| 			if (y + y_off >= title_bar_height()) | ||||
| 				break; | ||||
| 			for (int32_t x = 0; (uint32_t)x < font.width(); x++) | ||||
| 			{ | ||||
| 				if (x + x_off >= text_area.width) | ||||
| 					break; | ||||
| 				const uint8_t bitmask = 1 << (font.width() - x - 1); | ||||
| 				if (glyph[y * font.pitch()] & bitmask) | ||||
| 					title_bar_data[(y_off + y) * title_bar_width() + (x_off + x)] = 0x000000; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_title_bar_data) | ||||
| 		delete[] m_title_bar_data; | ||||
| 	m_title_bar_data = title_bar_data; | ||||
| } | ||||
|  | @ -3,13 +3,15 @@ | |||
| #include "Utils.h" | ||||
| 
 | ||||
| #include <BAN/RefPtr.h> | ||||
| #include <BAN/String.h> | ||||
| 
 | ||||
| #include <LibFont/Font.h> | ||||
| 
 | ||||
| class Window : public BAN::RefCounted<Window> | ||||
| { | ||||
| public: | ||||
| 	Window(int fd) | ||||
| 		: m_client_fd(fd) | ||||
| 	{ } | ||||
| 	Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font); | ||||
| 	~Window(); | ||||
| 
 | ||||
| 	void set_position(Position position) | ||||
| 	{ | ||||
|  | @ -17,16 +19,6 @@ public: | |||
| 		m_client_area.y = position.y; | ||||
| 	} | ||||
| 
 | ||||
| 	void set_size(Position size, uint32_t* fb_addr) | ||||
| 	{ | ||||
| 		m_client_area.width = size.x; | ||||
| 		m_client_area.height = size.y; | ||||
| 		m_fb_addr = fb_addr; | ||||
| 	} | ||||
| 
 | ||||
| 	bool is_deleted() const { return m_deleted; } | ||||
| 	void mark_deleted() { m_deleted = true; } | ||||
| 
 | ||||
| 	int client_fd() const { return m_client_fd; } | ||||
| 
 | ||||
| 	int32_t client_x() const { return m_client_area.x; } | ||||
|  | @ -56,24 +48,29 @@ public: | |||
| 	{ | ||||
| 		ASSERT(title_bar_area().contains({ abs_x, abs_y })); | ||||
| 
 | ||||
| 		Rectangle close_button = { | ||||
| 			title_bar_x() + title_bar_width() - title_bar_height() + 1, | ||||
| 			title_bar_y() + 1, | ||||
| 			title_bar_height() - 2, | ||||
| 			title_bar_height() - 2 | ||||
| 		}; | ||||
| 		if (auto close_button = close_button_area(); close_button.contains({ abs_x, abs_y })) | ||||
| 			return close_button.contains(cursor) ? 0xFF0000 : 0xD00000; | ||||
| 
 | ||||
| 		if (close_button.contains({ abs_x, abs_y })) | ||||
| 			return close_button.contains(cursor) ? 0xFF0000 : 0xA00000; | ||||
| 
 | ||||
| 		return 0xFFFFFF; | ||||
| 		int32_t rel_x = abs_x - title_bar_x(); | ||||
| 		int32_t rel_y = abs_y - title_bar_y(); | ||||
| 		return m_title_bar_data[rel_y * title_bar_width() + rel_x]; | ||||
| 	} | ||||
| 
 | ||||
| 	Circle close_button_area() const { return { title_bar_x() + title_bar_width() - title_bar_height() / 2, title_bar_y() + title_bar_height() / 2, title_bar_height() * 3 / 8 }; } | ||||
| 	Rectangle title_text_area() const { return { title_bar_x(), title_bar_y(), title_bar_width() - title_bar_height(), title_bar_height() }; } | ||||
| 
 | ||||
| private: | ||||
| 	void prepare_title_bar(const LibFont::Font& font); | ||||
| 
 | ||||
| private: | ||||
| 	static constexpr int32_t m_title_bar_height { 20 }; | ||||
| 
 | ||||
| 	const int	m_client_fd		{ -1 }; | ||||
| 	uint32_t*	m_fb_addr		{ nullptr }; | ||||
| 	Rectangle	m_client_area	{ 0, 0, 0, 0 }; | ||||
| 	bool		m_deleted		{ false }; | ||||
| 	long		m_smo_key		{ 0 }; | ||||
| 	uint32_t*	m_fb_addr		{ nullptr }; | ||||
| 	uint32_t*	m_title_bar_data { nullptr }; | ||||
| 	BAN::String m_title; | ||||
| 
 | ||||
| 	friend class BAN::RefPtr<Window>; | ||||
| }; | ||||
|  |  | |||
|  | @ -1,44 +1,110 @@ | |||
| #include "Cursor.h" | ||||
| #include "WindowServer.h" | ||||
| 
 | ||||
| #include <BAN/Debug.h> | ||||
| 
 | ||||
| #include <LibGUI/Window.h> | ||||
| #include <LibInput/KeyboardLayout.h> | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <sys/banan-os.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/socket.h> | ||||
| 
 | ||||
| void WindowServer::add_window(int fd, BAN::RefPtr<Window> window) | ||||
| void WindowServer::on_window_packet(int fd, LibGUI::WindowPacket packet) | ||||
| { | ||||
| 	MUST(m_windows_ordered.insert(0, window)); | ||||
| 	MUST(m_windows.insert(fd, window)); | ||||
| 	set_focused_window(window); | ||||
| } | ||||
| 	switch (packet.type) | ||||
| 	{ | ||||
| 		case LibGUI::WindowPacketType::CreateWindow: | ||||
| 		{ | ||||
| 			// FIXME: This should be probably allowed
 | ||||
| 			for (auto& window : m_client_windows) | ||||
| 			{ | ||||
| 				if (window->client_fd() == fd) | ||||
| 				{ | ||||
| 					dwarnln("client {} tried to create window while already owning a window", fd); | ||||
| 					return; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| void WindowServer::for_each_window(const BAN::Function<BAN::Iteration(int, Window&)>& callback) | ||||
| { | ||||
| 	BAN::Vector<int> deleted_windows; | ||||
| 	for (auto it = m_windows.begin(); it != m_windows.end(); it++) | ||||
| 			const size_t window_fb_bytes = packet.create.width * packet.create.height * 4; | ||||
| 
 | ||||
| 			long smo_key = smo_create(window_fb_bytes, PROT_READ | PROT_WRITE); | ||||
| 			if (smo_key == -1) | ||||
| 			{ | ||||
| 		auto ret = callback(it->key, *it->value); | ||||
| 		if (it->value->is_deleted()) | ||||
| 			MUST(deleted_windows.push_back(it->key)); | ||||
| 		if (ret == BAN::Iteration::Break) | ||||
| 				dwarnln("smo_create: {}", strerror(errno)); | ||||
| 				break; | ||||
| 		ASSERT(ret == BAN::Iteration::Continue); | ||||
| 			} | ||||
| 	for (int fd : deleted_windows) | ||||
| 
 | ||||
| 			Rectangle window_area { | ||||
| 				static_cast<int32_t>((m_framebuffer.width - packet.create.width) / 2), | ||||
| 				static_cast<int32_t>((m_framebuffer.height - packet.create.height) / 2), | ||||
| 				static_cast<int32_t>(packet.create.width), | ||||
| 				static_cast<int32_t>(packet.create.height) | ||||
| 			}; | ||||
| 
 | ||||
| 			packet.create.title[sizeof(packet.create.title) - 1] = '\0'; | ||||
| 
 | ||||
| 			// Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font)
 | ||||
| 			auto window = MUST(BAN::RefPtr<Window>::create( | ||||
| 				fd, | ||||
| 				window_area, | ||||
| 				smo_key, | ||||
| 				packet.create.title, | ||||
| 				m_font | ||||
| 			)); | ||||
| 			MUST(m_client_windows.push_back(window)); | ||||
| 			set_focused_window(window); | ||||
| 
 | ||||
| 			LibGUI::WindowCreateResponse response; | ||||
| 			response.framebuffer_smo_key = smo_key; | ||||
| 			if (send(window->client_fd(), &response, sizeof(response), 0) != sizeof(response)) | ||||
| 			{ | ||||
| 		auto window = m_windows[fd]; | ||||
| 		m_windows.remove(fd); | ||||
| 		for (size_t i = 0; i < m_windows_ordered.size(); i++) | ||||
| 				dwarnln("send: {}", strerror(errno)); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			break; | ||||
| 		} | ||||
| 		case LibGUI::WindowPacketType::Invalidate: | ||||
| 		{ | ||||
| 			if (m_windows_ordered[i] == window) | ||||
| 			if (packet.invalidate.width == 0 || packet.invalidate.height == 0) | ||||
| 				break; | ||||
| 
 | ||||
| 			BAN::RefPtr<Window> target_window; | ||||
| 			for (auto& window : m_client_windows) | ||||
| 			{ | ||||
| 				m_windows_ordered.remove(i); | ||||
| 				if (window->client_fd() == fd) | ||||
| 				{ | ||||
| 					target_window = window; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			if (!target_window) | ||||
| 			{ | ||||
| 				dwarnln("client {} tried to invalidate window while not owning a window", fd); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			const int32_t br_x = packet.invalidate.x + packet.invalidate.width - 1; | ||||
| 			const int32_t br_y = packet.invalidate.y + packet.invalidate.height - 1; | ||||
| 			if (!target_window->client_size().contains({ br_x, br_y })) | ||||
| 			{ | ||||
| 				dwarnln("Invalid Invalidate packet parameters"); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			invalidate({ | ||||
| 				target_window->client_x() + static_cast<int32_t>(packet.invalidate.x), | ||||
| 				target_window->client_y() + static_cast<int32_t>(packet.invalidate.y), | ||||
| 				static_cast<int32_t>(packet.invalidate.width), | ||||
| 				static_cast<int32_t>(packet.invalidate.height), | ||||
| 			}); | ||||
| 
 | ||||
| 			break; | ||||
| 		} | ||||
| 		default: | ||||
| 			ASSERT_NOT_REACHED(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -53,7 +119,18 @@ void WindowServer::on_key_event(LibInput::KeyEvent event) | |||
| 
 | ||||
| 	// Quick hack to stop the window server
 | ||||
| 	if (event.pressed() && event.key == LibInput::Key::Escape) | ||||
| 		exit(0); | ||||
| 	{ | ||||
| 		m_is_stopped = true; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	// Kill window with mod+Q
 | ||||
| 	if (m_is_mod_key_held && event.pressed() && event.key == LibInput::Key::Q) | ||||
| 	{ | ||||
| 		if (m_focused_window) | ||||
| 			remove_client_fd(m_focused_window->client_fd()); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (m_focused_window) | ||||
| 	{ | ||||
|  | @ -67,11 +144,11 @@ void WindowServer::on_key_event(LibInput::KeyEvent event) | |||
| void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) | ||||
| { | ||||
| 	BAN::RefPtr<Window> target_window; | ||||
| 	for (size_t i = m_windows_ordered.size(); i > 0; i--) | ||||
| 	for (size_t i = m_client_windows.size(); i > 0; i--) | ||||
| 	{ | ||||
| 		if (m_windows_ordered[i - 1]->full_area().contains(m_cursor)) | ||||
| 		if (m_client_windows[i - 1]->full_area().contains(m_cursor)) | ||||
| 		{ | ||||
| 			target_window = m_windows_ordered[i - 1]; | ||||
| 			target_window = m_client_windows[i - 1]; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | @ -83,10 +160,18 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) | |||
| 	set_focused_window(target_window); | ||||
| 
 | ||||
| 	// Handle window moving when mod key is held or mouse press on title bar
 | ||||
| 	if (event.pressed && event.button == LibInput::MouseButton::Left && !m_is_moving_window && (target_window->title_bar_area().contains(m_cursor) || m_is_mod_key_held)) | ||||
| 	const bool can_start_move = m_is_mod_key_held || target_window->title_text_area().contains(m_cursor); | ||||
| 	if (event.pressed && event.button == LibInput::MouseButton::Left && !m_is_moving_window && can_start_move) | ||||
| 		m_is_moving_window = true; | ||||
| 	else if (m_is_moving_window && !event.pressed) | ||||
| 		m_is_moving_window = false; | ||||
| 	else if (!event.pressed && event.button == LibInput::MouseButton::Left && target_window->close_button_area().contains(m_cursor)) | ||||
| 	{ | ||||
| 		// NOTE: we always have target window if code reaches here
 | ||||
| 		LibGUI::EventPacket packet; | ||||
| 		packet.type = LibGUI::EventPacket::Type::CloseWindow; | ||||
| 		send(m_focused_window->client_fd(), &packet, sizeof(packet), 0); | ||||
| 	} | ||||
| 	else if (target_window->client_area().contains(m_cursor)) | ||||
| 	{ | ||||
| 		// NOTE: we always have target window if code reaches here
 | ||||
|  | @ -119,7 +204,7 @@ void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event) | |||
| 	invalidate(new_cursor); | ||||
| 
 | ||||
| 	// TODO: Really no need to loop over every window
 | ||||
| 	for (auto& window : m_windows_ordered) | ||||
| 	for (auto& window : m_client_windows) | ||||
| 	{ | ||||
| 		auto title_bar = window->title_bar_area(); | ||||
| 		if (title_bar.get_overlap(old_cursor).has_value() || title_bar.get_overlap(new_cursor).has_value()) | ||||
|  | @ -165,13 +250,13 @@ void WindowServer::set_focused_window(BAN::RefPtr<Window> window) | |||
| 	if (m_focused_window == window) | ||||
| 		return; | ||||
| 
 | ||||
| 	for (size_t i = m_windows_ordered.size(); i > 0; i--) | ||||
| 	for (size_t i = m_client_windows.size(); i > 0; i--) | ||||
| 	{ | ||||
| 		if (m_windows_ordered[i - 1] == window) | ||||
| 		if (m_client_windows[i - 1] == window) | ||||
| 		{ | ||||
| 			m_focused_window = window; | ||||
| 			m_windows_ordered.remove(i - 1); | ||||
| 			MUST(m_windows_ordered.push_back(window)); | ||||
| 			m_client_windows.remove(i - 1); | ||||
| 			MUST(m_client_windows.push_back(window)); | ||||
| 			invalidate(window->full_area()); | ||||
| 			break; | ||||
| 		} | ||||
|  | @ -186,9 +271,9 @@ void WindowServer::invalidate(Rectangle area) | |||
| 	area = fb_overlap.release_value(); | ||||
| 
 | ||||
| 	for (int32_t y = area.y; y < area.y + area.height; y++) | ||||
| 		memset(&m_framebuffer.mmap[y * m_framebuffer.width + area.x], 0, area.width * 4); | ||||
| 		memset(&m_framebuffer.mmap[y * m_framebuffer.width + area.x], 0x10, area.width * 4); | ||||
| 
 | ||||
| 	for (auto& pwindow : m_windows_ordered) | ||||
| 	for (auto& pwindow : m_client_windows) | ||||
| 	{ | ||||
| 		auto& window = *pwindow; | ||||
| 
 | ||||
|  | @ -255,3 +340,65 @@ Rectangle WindowServer::cursor_area() const | |||
| { | ||||
| 	return { m_cursor.x, m_cursor.y, s_cursor_width, s_cursor_height }; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void WindowServer::add_client_fd(int fd) | ||||
| { | ||||
| 	MUST(m_client_fds.push_back(fd)); | ||||
| } | ||||
| 
 | ||||
| void WindowServer::remove_client_fd(int fd) | ||||
| { | ||||
| 	for (size_t i = 0; i < m_client_fds.size(); i++) | ||||
| 	{ | ||||
| 		if (m_client_fds[i] == fd) | ||||
| 		{ | ||||
| 			m_client_fds.remove(i); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (size_t i = 0; i < m_client_windows.size(); i++) | ||||
| 	{ | ||||
| 		auto window = m_client_windows[i]; | ||||
| 		if (window->client_fd() == fd) | ||||
| 		{ | ||||
| 			auto window_area = window->full_area(); | ||||
| 			m_client_windows.remove(i); | ||||
| 			invalidate(window_area); | ||||
| 
 | ||||
| 			if (window == m_focused_window) | ||||
| 			{ | ||||
| 				m_focused_window = nullptr; | ||||
| 				if (!m_client_windows.empty()) | ||||
| 					set_focused_window(m_client_windows.back()); | ||||
| 			} | ||||
| 
 | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	m_deleted_window = true; | ||||
| } | ||||
| 
 | ||||
| int WindowServer::get_client_fds(fd_set& fds) const | ||||
| { | ||||
| 	int max_fd = 0; | ||||
| 	for (int fd : m_client_fds) | ||||
| 	{ | ||||
| 		FD_SET(fd, &fds); | ||||
| 		max_fd = BAN::Math::max(max_fd, fd); | ||||
| 	} | ||||
| 	return max_fd; | ||||
| } | ||||
| 
 | ||||
| void WindowServer::for_each_client_fd(const BAN::Function<BAN::Iteration(int)>& callback) | ||||
| { | ||||
| 	m_deleted_window = false; | ||||
| 	for (int fd : m_client_fds) | ||||
| 	{ | ||||
| 		if (m_deleted_window) | ||||
| 			break; | ||||
| 		callback(fd); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -8,21 +8,25 @@ | |||
| #include <BAN/Vector.h> | ||||
| #include <BAN/HashMap.h> | ||||
| 
 | ||||
| #include <LibFont/Font.h> | ||||
| #include <LibGUI/Window.h> | ||||
| #include <LibInput/KeyEvent.h> | ||||
| #include <LibInput/MouseEvent.h> | ||||
| 
 | ||||
| #include <sys/select.h> | ||||
| 
 | ||||
| class WindowServer | ||||
| { | ||||
| public: | ||||
| 	WindowServer(Framebuffer& framebuffer) | ||||
| 		: m_framebuffer(framebuffer) | ||||
| 		, m_cursor({ framebuffer.width / 2, framebuffer.height / 2 }) | ||||
| 		, m_font(MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"sv))) | ||||
| 	{ | ||||
| 		invalidate(m_framebuffer.area()); | ||||
| 	} | ||||
| 
 | ||||
| 	void add_window(int fd, BAN::RefPtr<Window> window); | ||||
| 	void for_each_window(const BAN::Function<BAN::Iteration(int, Window&)>& callback); | ||||
| 	void on_window_packet(int fd, LibGUI::WindowPacket); | ||||
| 
 | ||||
| 	void on_key_event(LibInput::KeyEvent event); | ||||
| 	void on_mouse_button(LibInput::MouseButtonEvent event); | ||||
|  | @ -34,13 +38,25 @@ public: | |||
| 
 | ||||
| 	Rectangle cursor_area() const; | ||||
| 
 | ||||
| 	void add_client_fd(int fd); | ||||
| 	void remove_client_fd(int fd); | ||||
| 	int get_client_fds(fd_set& fds) const; | ||||
| 	void for_each_client_fd(const BAN::Function<BAN::Iteration(int)>& callback); | ||||
| 
 | ||||
| 	bool is_stopped() const { return m_is_stopped; } | ||||
| 
 | ||||
| private: | ||||
| 	Framebuffer& m_framebuffer; | ||||
| 	BAN::Vector<BAN::RefPtr<Window>> m_windows_ordered; | ||||
| 	BAN::HashMap<int, BAN::RefPtr<Window>> m_windows; | ||||
| 	BAN::Vector<BAN::RefPtr<Window>> m_client_windows; | ||||
| 	BAN::Vector<int> m_client_fds; | ||||
| 
 | ||||
| 	bool m_is_mod_key_held { false }; | ||||
| 	bool m_is_moving_window { false }; | ||||
| 	BAN::RefPtr<Window> m_focused_window; | ||||
| 	Position m_cursor; | ||||
| 
 | ||||
| 	bool m_deleted_window { false }; | ||||
| 	bool m_is_stopped { false }; | ||||
| 
 | ||||
| 	LibFont::Font m_font; | ||||
| }; | ||||
|  |  | |||
|  | @ -85,19 +85,16 @@ int main() | |||
| 
 | ||||
| 	dprintln("Window server started"); | ||||
| 
 | ||||
| 	for (int i = 0; i < 2; i++) | ||||
| 	{ | ||||
| 		if (fork() == 0) | ||||
| 		{ | ||||
| 			execl("/bin/test-window", "test-window", NULL); | ||||
| 			exit(1); | ||||
| 		} | ||||
| 	} | ||||
| 	size_t window_packet_sizes[LibGUI::WindowPacketType::COUNT] {}; | ||||
| 	window_packet_sizes[LibGUI::WindowPacketType::INVALID]		= 0; | ||||
| 	window_packet_sizes[LibGUI::WindowPacketType::CreateWindow]	= sizeof(LibGUI::WindowCreatePacket); | ||||
| 	window_packet_sizes[LibGUI::WindowPacketType::Invalidate]	= sizeof(LibGUI::WindowInvalidatePacket); | ||||
| 	static_assert(LibGUI::WindowPacketType::COUNT == 3); | ||||
| 
 | ||||
| 	WindowServer window_server(framebuffer); | ||||
| 	for (;;) | ||||
| 	while (!window_server.is_stopped()) | ||||
| 	{ | ||||
| 		int max_socket = server_fd; | ||||
| 		int max_fd = server_fd; | ||||
| 
 | ||||
| 		fd_set fds; | ||||
| 		FD_ZERO(&fds); | ||||
|  | @ -105,23 +102,16 @@ int main() | |||
| 		if (keyboard_fd != -1) | ||||
| 		{ | ||||
| 			FD_SET(keyboard_fd, &fds); | ||||
| 			max_socket = BAN::Math::max(max_socket, keyboard_fd); | ||||
| 			max_fd = BAN::Math::max(max_fd, keyboard_fd); | ||||
| 		} | ||||
| 		if (mouse_fd != -1) | ||||
| 		{ | ||||
| 			FD_SET(mouse_fd, &fds); | ||||
| 			max_socket = BAN::Math::max(max_socket, mouse_fd); | ||||
| 			max_fd = BAN::Math::max(max_fd, mouse_fd); | ||||
| 		} | ||||
| 		window_server.for_each_window( | ||||
| 			[&](int fd, Window&) -> BAN::Iteration | ||||
| 			{ | ||||
| 				FD_SET(fd, &fds); | ||||
| 				max_socket = BAN::Math::max(max_socket, fd); | ||||
| 				return BAN::Iteration::Continue; | ||||
| 			} | ||||
| 		); | ||||
| 		max_fd = BAN::Math::max(max_fd, window_server.get_client_fds(fds)); | ||||
| 
 | ||||
| 		if (select(max_socket + 1, &fds, nullptr, nullptr, nullptr) == -1) | ||||
| 		if (select(max_fd + 1, &fds, nullptr, nullptr, nullptr) == -1) | ||||
| 		{ | ||||
| 			dwarnln("select: {}", strerror(errno)); | ||||
| 			break; | ||||
|  | @ -135,8 +125,7 @@ int main() | |||
| 				dwarnln("accept: {}", strerror(errno)); | ||||
| 				continue; | ||||
| 			} | ||||
| 			auto window = MUST(BAN::RefPtr<Window>::create(window_fd)); | ||||
| 			window_server.add_window(window_fd, window); | ||||
| 			window_server.add_client_fd(window_fd); | ||||
| 		} | ||||
| 
 | ||||
| 		if (keyboard_fd != -1 && FD_ISSET(keyboard_fd, &fds)) | ||||
|  | @ -172,8 +161,8 @@ int main() | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		window_server.for_each_window( | ||||
| 			[&](int fd, Window& window) -> BAN::Iteration | ||||
| 		window_server.for_each_client_fd( | ||||
| 			[&](int fd) -> BAN::Iteration | ||||
| 			{ | ||||
| 				if (!FD_ISSET(fd, &fds)) | ||||
| 					return BAN::Iteration::Continue; | ||||
|  | @ -184,89 +173,16 @@ int main() | |||
| 					dwarnln("recv: {}", strerror(errno)); | ||||
| 				if (nrecv <= 0) | ||||
| 				{ | ||||
| 					window.mark_deleted(); | ||||
| 					window_server.remove_client_fd(fd); | ||||
| 					return BAN::Iteration::Continue; | ||||
| 				} | ||||
| 
 | ||||
| 				switch (packet.type) | ||||
| 				{ | ||||
| 					case LibGUI::WindowPacketType::CreateWindow: | ||||
| 					{ | ||||
| 						if (nrecv != sizeof(LibGUI::WindowCreatePacket)) | ||||
| 						{ | ||||
| 							dwarnln("Invalid WindowCreate packet size"); | ||||
| 							break; | ||||
| 						} | ||||
| 
 | ||||
| 						const size_t window_fb_bytes = packet.create.width * packet.create.height * 4; | ||||
| 
 | ||||
| 						long smo_key = smo_create(window_fb_bytes, PROT_READ | PROT_WRITE); | ||||
| 						if (smo_key == -1) | ||||
| 						{ | ||||
| 							dwarnln("smo_create: {}", strerror(errno)); | ||||
| 							break; | ||||
| 						} | ||||
| 
 | ||||
| 						void* smo_address = smo_map(smo_key); | ||||
| 						if (smo_address == nullptr) | ||||
| 						{ | ||||
| 							dwarnln("smo_map: {}", strerror(errno)); | ||||
| 							break; | ||||
| 						} | ||||
| 						memset(smo_address, 0, window_fb_bytes); | ||||
| 
 | ||||
| 						LibGUI::WindowCreateResponse response; | ||||
| 						response.framebuffer_smo_key = smo_key; | ||||
| 						if (send(fd, &response, sizeof(response), 0) != sizeof(response)) | ||||
| 						{ | ||||
| 							dwarnln("send: {}", strerror(errno)); | ||||
| 							break; | ||||
| 						} | ||||
| 
 | ||||
| 						window.set_size({ | ||||
| 							static_cast<int32_t>(packet.create.width), | ||||
| 							static_cast<int32_t>(packet.create.height) | ||||
| 						}, reinterpret_cast<uint32_t*>(smo_address)); | ||||
| 						window.set_position({ | ||||
| 							static_cast<int32_t>((framebuffer.width - window.client_width()) / 2), | ||||
| 							static_cast<int32_t>((framebuffer.height - window.client_height()) / 2) | ||||
| 						}); | ||||
| 						window_server.invalidate(window.full_area()); | ||||
| 
 | ||||
| 						break; | ||||
| 					} | ||||
| 					case LibGUI::WindowPacketType::Invalidate: | ||||
| 					{ | ||||
| 						if (nrecv != sizeof(LibGUI::WindowInvalidatePacket)) | ||||
| 						{ | ||||
| 							dwarnln("Invalid Invalidate packet size"); | ||||
| 							break; | ||||
| 						} | ||||
| 
 | ||||
| 						if (packet.invalidate.width == 0 || packet.invalidate.height == 0) | ||||
| 							break; | ||||
| 
 | ||||
| 						const int32_t br_x = packet.invalidate.x + packet.invalidate.width - 1; | ||||
| 						const int32_t br_y = packet.invalidate.y + packet.invalidate.height - 1; | ||||
| 						if (!window.client_size().contains({ br_x, br_y })) | ||||
| 						{ | ||||
| 							dwarnln("Invalid Invalidate packet parameters"); | ||||
| 							break; | ||||
| 						} | ||||
| 
 | ||||
| 						window_server.invalidate({ | ||||
| 							window.client_x() + static_cast<int32_t>(packet.invalidate.x), | ||||
| 							window.client_y() + static_cast<int32_t>(packet.invalidate.y), | ||||
| 							static_cast<int32_t>(packet.invalidate.width), | ||||
| 							static_cast<int32_t>(packet.invalidate.height), | ||||
| 						}); | ||||
| 
 | ||||
| 						break; | ||||
| 					} | ||||
| 					default: | ||||
| 						dwarnln("Invalid window packet from {}", fd); | ||||
| 				} | ||||
| 
 | ||||
| 				if (packet.type == LibGUI::WindowPacketType::INVALID || packet.type >= LibGUI::WindowPacketType::COUNT) | ||||
| 					dwarnln("Invalid WindowPacket (type {})", (int)packet.type); | ||||
| 				if (static_cast<size_t>(nrecv) != window_packet_sizes[packet.type]) | ||||
| 					dwarnln("Invalid WindowPacket size (type {}, size {})", (int)packet.type, nrecv); | ||||
| 				else | ||||
| 					window_server.on_window_packet(fd, packet); | ||||
| 				return BAN::Iteration::Continue; | ||||
| 			} | ||||
| 		); | ||||
|  |  | |||
|  | @ -20,14 +20,17 @@ int main() | |||
| 	clock_gettime(CLOCK_MONOTONIC, &ts); | ||||
| 	srand(ts.tv_nsec); | ||||
| 
 | ||||
| 	auto window_or_error = LibGUI::Window::create(300, 200); | ||||
| 	auto window_or_error = LibGUI::Window::create(300, 200, "test-window"); | ||||
| 	if (window_or_error.is_error()) | ||||
| 	{ | ||||
| 		dprintln("{}", window_or_error.error()); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	bool running = true; | ||||
| 
 | ||||
| 	auto window = window_or_error.release_value(); | ||||
| 	window->set_close_window_event_callback([&] { running = false; }); | ||||
| 	window->set_mouse_button_event_callback( | ||||
| 		[&](LibGUI::EventPacket::MouseButtonEvent event) | ||||
| 		{ | ||||
|  | @ -49,7 +52,7 @@ int main() | |||
| 
 | ||||
| 	randomize_color(window); | ||||
| 
 | ||||
| 	for (;;) | ||||
| 	while (running) | ||||
| 	{ | ||||
| 		window->poll_events(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue