Merge remote-tracking branch 'origin/master' into serenity-keys

This commit is contained in:
faissaloo 2019-06-15 21:55:17 +01:00
commit b635c3db54
Notes: sideshowbarker 2024-07-19 13:35:36 +09:00
527 changed files with 9637 additions and 5614 deletions

View file

@ -10,4 +10,3 @@ IndentPPDirectives: AfterHash
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
AfterEnum: true

2
.gitignore vendored
View file

@ -12,3 +12,5 @@ Toolchain/Tarballs
Toolchain/Build
Toolchain/Local
.vscode
compile_commands.json
.clang_complete

View file

@ -16,7 +16,7 @@ notifications:
before_install:
- sudo apt-get update
- sudo apt-get install -y libmpfr-dev libmpc-dev libgmp-dev
- sudo apt-get install -y e2fsprogs qemu-system-i386
- sudo apt-get install -y e2fsprogs qemu-system-i386 qemu-utils
script:
- cd Toolchain

View file

@ -10,15 +10,38 @@
namespace AK {
// String is a convenience wrapper around StringImpl, suitable for passing
// around as a value type. It's basically the same as passing around a
// RetainPtr<StringImpl>, with a bit of syntactic sugar.
//
// Note that StringImpl is an immutable object that cannot shrink or grow.
// Its allocation size is snugly tailored to the specific string it contains.
// Copying a String is very efficient, since the internal StringImpl is
// retainable and so copying only requires modifying the retain count.
//
// There are three main ways to construct a new String:
//
// s = String("some literal");
//
// s = String::format("%d little piggies", m_piggies);
//
// StringBuilder builder;
// builder.append("abc");
// builder.append("123");
// s = builder.to_string();
class String {
public:
~String() {}
String() {}
String(StringView view)
: m_impl(StringImpl::create(view.characters(), view.length()))
String(const StringView& view)
{
if (view.m_impl)
m_impl = *view.m_impl;
else
m_impl = StringImpl::create(view.characters(), view.length());
}
String(const String& other)
@ -36,7 +59,7 @@ public:
{
}
String(const char* cstring, ssize_t length, ShouldChomp shouldChomp = NoChomp)
String(const char* cstring, int length, ShouldChomp shouldChomp = NoChomp)
: m_impl(StringImpl::create(cstring, length, shouldChomp))
{
}
@ -67,7 +90,7 @@ public:
};
static String repeated(char, int count);
bool matches(const String& pattern, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
bool matches(const StringView& pattern, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
int to_int(bool& ok) const;
unsigned to_uint(bool& ok) const;
@ -86,6 +109,7 @@ public:
return m_impl->to_uppercase();
}
Vector<String> split_limit(char separator, int limit) const;
Vector<String> split(char separator) const;
String substring(int start, int length) const;
@ -94,20 +118,35 @@ public:
bool is_null() const { return !m_impl; }
bool is_empty() const { return length() == 0; }
ssize_t length() const { return m_impl ? m_impl->length() : 0; }
int length() const { return m_impl ? m_impl->length() : 0; }
const char* characters() const { return m_impl ? m_impl->characters() : nullptr; }
char operator[](ssize_t i) const
char operator[](int i) const
{
ASSERT(m_impl);
return (*m_impl)[i];
}
bool ends_with(const String&) const;
bool starts_with(const StringView&) const;
bool ends_with(const StringView&) const;
bool operator==(const String&) const;
bool operator!=(const String& other) const { return !(*this == other); }
bool operator<(const String&) const;
bool operator==(const char* cstring) const
{
if (is_null())
return !cstring;
if (!cstring)
return false;
return !strcmp(characters(), cstring);
}
bool operator!=(const char* cstring) const
{
return !(*this == cstring);
}
String isolated_copy() const;
static String empty();
@ -146,7 +185,7 @@ public:
StringView view() const { return { characters(), length() }; }
private:
bool match_helper(const String& mask) const;
bool match_helper(const StringView& mask) const;
RetainPtr<StringImpl> m_impl;
};

View file

@ -1,7 +1,20 @@
#pragma once
namespace AK {
template<typename T>
class Badge {
friend T;
Badge() {}
Badge(const Badge&) = delete;
Badge& operator=(const Badge&) = delete;
Badge(Badge&&) = delete;
Badge& operator=(Badge&&) = delete;
};
}
using AK::Badge;

View file

@ -20,6 +20,11 @@ public:
return Bitmap(size, default_value);
}
static Bitmap create()
{
return Bitmap();
}
~Bitmap()
{
if (m_owned)
@ -45,12 +50,74 @@ public:
byte* data() { return m_data; }
const byte* data() const { return m_data; }
void grow(int size, bool default_value)
{
ASSERT(size > m_size);
auto previous_size_bytes = size_in_bytes();
auto previous_size = m_size;
auto previous_data = m_data;
m_size = size;
m_data = reinterpret_cast<byte*>(kmalloc(size_in_bytes()));
fill(default_value);
if (previous_data != nullptr) {
memcpy(m_data, previous_data, previous_size_bytes);
if ((previous_size % 8) != 0) {
if (default_value)
m_data[previous_size_bytes - 1] |= (0xff >> (previous_size % 8));
else
m_data[previous_size_bytes - 1] &= ~(0xff >> (previous_size % 8));
}
kfree(previous_data);
}
}
void fill(bool value)
{
memset(m_data, value ? 0xff : 0x00, size_in_bytes());
}
int find_first_set() const
{
int i = 0;
while (i < m_size / 8 && m_data[i] == 0x00)
i++;
int j = 0;
for (j = i * 8; j < m_size; j++)
if (get(j))
return j;
return -1;
}
int find_first_unset() const
{
int i = 0;
while (i < m_size / 8 && m_data[i] == 0xff)
i++;
int j = 0;
for (j = i * 8; j < m_size; j++)
if (!get(j))
return j;
return -1;
}
private:
explicit Bitmap()
: m_size(0)
, m_owned(true)
{
m_data = nullptr;
}
explicit Bitmap(int size, bool default_value)
: m_size(size)
, m_owned(true)

View file

@ -36,15 +36,7 @@ public:
m_buffer[m_offset++] = (byte)(value >> 24) & 0xffu;
}
void operator<<(const char* str)
{
ssize_t len = strlen(str);
ASSERT(len >= 0);
for (ssize_t i = 0; i < len; ++i)
m_buffer[m_offset++] = str[i];
}
void operator<<(const String& value)
void operator<<(const StringView& value)
{
for (ssize_t i = 0; i < value.length(); ++i)
m_buffer[m_offset++] = value[i];

View file

@ -100,12 +100,12 @@ public:
return *this;
}
static ByteBuffer create_uninitialized(ssize_t size) { return ByteBuffer(ByteBufferImpl::create_uninitialized(size)); }
static ByteBuffer create_zeroed(ssize_t size) { return ByteBuffer(ByteBufferImpl::create_zeroed(size)); }
static ByteBuffer copy(const void* data, ssize_t size) { return ByteBuffer(ByteBufferImpl::copy(data, size)); }
static ByteBuffer wrap(const void* data, ssize_t size) { return ByteBuffer(ByteBufferImpl::wrap(data, size)); }
static ByteBuffer wrap(void* data, ssize_t size) { return ByteBuffer(ByteBufferImpl::wrap(data, size)); }
static ByteBuffer adopt(void* data, ssize_t size) { return ByteBuffer(ByteBufferImpl::adopt(data, size)); }
static ByteBuffer create_uninitialized(int size) { return ByteBuffer(ByteBufferImpl::create_uninitialized(size)); }
static ByteBuffer create_zeroed(int size) { return ByteBuffer(ByteBufferImpl::create_zeroed(size)); }
static ByteBuffer copy(const void* data, int size) { return ByteBuffer(ByteBufferImpl::copy(data, size)); }
static ByteBuffer wrap(const void* data, int size) { return ByteBuffer(ByteBufferImpl::wrap(data, size)); }
static ByteBuffer wrap(void* data, int size) { return ByteBuffer(ByteBufferImpl::wrap(data, size)); }
static ByteBuffer adopt(void* data, int size) { return ByteBuffer(ByteBufferImpl::adopt(data, size)); }
~ByteBuffer() { clear(); }
void clear() { m_impl = nullptr; }
@ -114,18 +114,18 @@ public:
bool operator!() const { return is_null(); }
bool is_null() const { return m_impl == nullptr; }
byte& operator[](ssize_t i)
byte& operator[](int i)
{
ASSERT(m_impl);
return (*m_impl)[i];
}
byte operator[](ssize_t i) const
byte operator[](int i) const
{
ASSERT(m_impl);
return (*m_impl)[i];
}
bool is_empty() const { return !m_impl || m_impl->is_empty(); }
ssize_t size() const { return m_impl ? m_impl->size() : 0; }
int size() const { return m_impl ? m_impl->size() : 0; }
byte* data() { return pointer(); }
const byte* data() const { return pointer(); }
@ -133,8 +133,8 @@ public:
byte* pointer() { return m_impl ? m_impl->pointer() : nullptr; }
const byte* pointer() const { return m_impl ? m_impl->pointer() : nullptr; }
byte* offset_pointer(ssize_t offset) { return m_impl ? m_impl->offset_pointer(offset) : nullptr; }
const byte* offset_pointer(ssize_t offset) const { return m_impl ? m_impl->offset_pointer(offset) : nullptr; }
byte* offset_pointer(int offset) { return m_impl ? m_impl->offset_pointer(offset) : nullptr; }
const byte* offset_pointer(int offset) const { return m_impl ? m_impl->offset_pointer(offset) : nullptr; }
void* end_pointer() { return m_impl ? m_impl->end_pointer() : nullptr; }
const void* end_pointer() const { return m_impl ? m_impl->end_pointer() : nullptr; }
@ -147,13 +147,13 @@ public:
}
// NOTE: trim() does not reallocate.
void trim(ssize_t size)
void trim(int size)
{
if (m_impl)
m_impl->trim(size);
}
ByteBuffer slice(ssize_t offset, ssize_t size) const
ByteBuffer slice(int offset, int size) const
{
if (is_null())
return {};
@ -164,7 +164,7 @@ public:
return copy(offset_pointer(offset), size);
}
void grow(ssize_t size)
void grow(int size)
{
if (!m_impl)
m_impl = ByteBufferImpl::create_uninitialized(size);
@ -204,7 +204,7 @@ inline ByteBufferImpl::ByteBufferImpl(const void* data, int size, ConstructionMo
m_owned = true;
}
inline ByteBufferImpl::ByteBufferImpl(void* data, ssize_t size, ConstructionMode mode)
inline ByteBufferImpl::ByteBufferImpl(void* data, int size, ConstructionMode mode)
: m_data(static_cast<byte*>(data))
, m_size(size)
{
@ -215,7 +215,7 @@ inline ByteBufferImpl::ByteBufferImpl(void* data, ssize_t size, ConstructionMode
}
}
inline void ByteBufferImpl::grow(ssize_t size)
inline void ByteBufferImpl::grow(int size)
{
ASSERT(size > m_size);
ASSERT(m_owned);

View file

@ -14,12 +14,18 @@ ELFImage::~ELFImage()
static const char* object_file_type_to_string(Elf32_Half type)
{
switch (type) {
case ET_NONE: return "None";
case ET_REL: return "Relocatable";
case ET_EXEC: return "Executable";
case ET_DYN: return "Shared object";
case ET_CORE: return "Core";
default: return "(?)";
case ET_NONE:
return "None";
case ET_REL:
return "Relocatable";
case ET_EXEC:
return "Executable";
case ET_DYN:
return "Shared object";
case ET_CORE:
return "Core";
default:
return "(?)";
}
}

View file

@ -1,9 +1,9 @@
#pragma once
#include <AK/OwnPtr.h>
#include <AK/HashMap.h>
#include <AK/AKString.h>
#include <AK/ELF/exec_elf.h>
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
class ELFImage {
public:
@ -27,7 +27,7 @@ public:
{
}
~Symbol() { }
~Symbol() {}
const char* name() const { return m_image.table_string(m_sym.st_name); }
unsigned section_index() const { return m_sym.st_shndx; }
@ -51,13 +51,13 @@ public:
, m_program_header_index(program_header_index)
{
}
~ProgramHeader() { }
~ProgramHeader() {}
unsigned index() const { return m_program_header_index; }
dword type() const { return m_program_header.p_type; }
dword flags() const { return m_program_header.p_flags; }
dword offset() const { return m_program_header.p_offset; }
LinearAddress laddr() const { return LinearAddress(m_program_header.p_vaddr); }
VirtualAddress vaddr() const { return VirtualAddress(m_program_header.p_vaddr); }
dword size_in_memory() const { return m_program_header.p_memsz; }
dword size_in_image() const { return m_program_header.p_filesz; }
dword alignment() const { return m_program_header.p_align; }
@ -65,6 +65,7 @@ public:
bool is_writable() const { return flags() & PF_W; }
bool is_executable() const { return flags() & PF_X; }
const char* raw_data() const { return m_image.raw_data(m_program_header.p_offset); }
private:
const ELFImage& m_image;
const Elf32_Phdr& m_program_header;
@ -79,7 +80,7 @@ public:
, m_section_index(sectionIndex)
{
}
~Section() { }
~Section() {}
const char* name() const { return m_image.section_header_table_string(m_section_header.sh_name); }
unsigned type() const { return m_section_header.sh_type; }
@ -109,15 +110,19 @@ public:
const Section section(unsigned) const;
const ProgramHeader program_header(unsigned const) const;
template<typename F> void for_each_section(F) const;
template<typename F> void for_each_section_of_type(unsigned, F) const;
template<typename F> void for_each_symbol(F) const;
template<typename F> void for_each_program_header(F) const;
template<typename F>
void for_each_section(F) const;
template<typename F>
void for_each_section_of_type(unsigned, F) const;
template<typename F>
void for_each_symbol(F) const;
template<typename F>
void for_each_program_header(F) const;
bool is_executable() const { return header().e_type == ET_EXEC; }
bool is_relocatable() const { return header().e_type == ET_REL; }
LinearAddress entry() const { return LinearAddress(header().e_entry); }
VirtualAddress entry() const { return VirtualAddress(header().e_entry); }
private:
bool parse_header();
@ -158,7 +163,7 @@ template<typename F>
inline void ELFImage::for_each_symbol(F func) const
{
for (unsigned i = 0; i < symbol_count(); ++i) {
if (func(symbol(i)) == IterationDecision::Abort)
if (func(symbol(i)) == IterationDecision::Break)
break;
}
}

View file

@ -1,6 +1,6 @@
#include "ELFLoader.h"
#include <AK/kstdio.h>
#include <AK/QuickSort.h>
#include <AK/kstdio.h>
//#define ELFLOADER_DEBUG
@ -30,33 +30,31 @@ bool ELFLoader::load()
bool ELFLoader::layout()
{
bool failed = false;
m_image.for_each_program_header([&] (const ELFImage::ProgramHeader& program_header) {
m_image.for_each_program_header([&](const ELFImage::ProgramHeader& program_header) {
if (program_header.type() != PT_LOAD)
return;
#ifdef ELFLOADER_DEBUG
kprintf("PH: L%x %u r:%u w:%u\n", program_header.laddr().get(), program_header.size_in_memory(), program_header.is_readable(), program_header.is_writable());
kprintf("PH: L%x %u r:%u w:%u\n", program_header.vaddr().get(), program_header.size_in_memory(), program_header.is_readable(), program_header.is_writable());
#endif
if (program_header.is_writable()) {
alloc_section_hook(
program_header.laddr(),
program_header.vaddr(),
program_header.size_in_memory(),
program_header.alignment(),
program_header.is_readable(),
program_header.is_writable(),
String::format("elf-alloc-%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "")
);
memcpy(program_header.laddr().as_ptr(), program_header.raw_data(), program_header.size_in_image());
String::format("elf-alloc-%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : ""));
memcpy(program_header.vaddr().as_ptr(), program_header.raw_data(), program_header.size_in_image());
} else {
map_section_hook(
program_header.laddr(),
program_header.vaddr(),
program_header.size_in_memory(),
program_header.alignment(),
program_header.offset(),
program_header.is_readable(),
program_header.is_writable(),
program_header.is_executable(),
String::format("elf-map-%s%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "", program_header.is_executable() ? "x" : "")
);
String::format("elf-map-%s%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "", program_header.is_executable() ? "x" : ""));
}
});
return !failed;
@ -65,7 +63,7 @@ bool ELFLoader::layout()
char* ELFLoader::symbol_ptr(const char* name)
{
char* found_ptr = nullptr;
m_image.for_each_symbol([&] (const ELFImage::Symbol symbol) {
m_image.for_each_symbol([&](const ELFImage::Symbol symbol) {
if (symbol.type() != STT_FUNC)
return IterationDecision::Continue;
if (strcmp(symbol.name(), name))
@ -74,7 +72,7 @@ char* ELFLoader::symbol_ptr(const char* name)
found_ptr = (char*)symbol.value();
else
ASSERT_NOT_REACHED();
return IterationDecision::Abort;
return IterationDecision::Break;
});
return found_ptr;
}
@ -83,11 +81,11 @@ String ELFLoader::symbolicate(dword address) const
{
if (m_sorted_symbols.is_empty()) {
m_sorted_symbols.ensure_capacity(m_image.symbol_count());
m_image.for_each_symbol([this] (auto& symbol) {
m_image.for_each_symbol([this](auto& symbol) {
m_sorted_symbols.append({ symbol.value(), symbol.name() });
return IterationDecision::Continue;
});
quick_sort(m_sorted_symbols.begin(), m_sorted_symbols.end(), [] (auto& a, auto& b) {
quick_sort(m_sorted_symbols.begin(), m_sorted_symbols.end(), [](auto& a, auto& b) {
return a.address < b.address;
});
}

View file

@ -5,7 +5,7 @@
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
#if defined(KERNEL)
#include <Kernel/LinearAddress.h>
# include <Kernel/VirtualAddress.h>
#endif
#include <AK/ELF/ELFImage.h>
@ -16,9 +16,9 @@ public:
bool load();
#if defined(KERNEL)
Function<void*(LinearAddress, size_t, size_t, bool, bool, const String&)> alloc_section_hook;
Function<void*(LinearAddress, size_t, size_t, size_t, bool r, bool w, bool x, const String&)> map_section_hook;
LinearAddress entry() const { return m_image.entry(); }
Function<void*(VirtualAddress, size_t, size_t, bool, bool, const String&)> alloc_section_hook;
Function<void*(VirtualAddress, size_t, size_t, size_t, bool r, bool w, bool x, const String&)> map_section_hook;
VirtualAddress entry() const { return m_image.entry(); }
#endif
char* symbol_ptr(const char* name);
@ -34,7 +34,7 @@ private:
char* area_for_section_name(const char*);
struct PtrAndSize {
PtrAndSize() { }
PtrAndSize() {}
PtrAndSize(char* p, unsigned s)
: ptr(p)
, size(s)
@ -52,4 +52,3 @@ private:
};
mutable Vector<SortedSymbol> m_sorted_symbols;
};

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
namespace AK {
FileSystemPath::FileSystemPath(const String& s)
FileSystemPath::FileSystemPath(const StringView& s)
: m_string(s)
{
m_is_valid = canonicalize();

View file

@ -7,12 +7,12 @@ namespace AK {
class FileSystemPath {
public:
FileSystemPath() {}
explicit FileSystemPath(const String&);
explicit FileSystemPath(const StringView&);
bool is_valid() const { return m_is_valid; }
String string() const { return m_string; }
const String& string() const { return m_string; }
String basename() const { return m_basename; }
const String& basename() const { return m_basename; }
const Vector<String>& parts() const { return m_parts; }

12
AK/IterationDecision.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
namespace AK {
enum class IterationDecision {
Continue,
Break,
};
}
using AK::IterationDecision;

View file

@ -9,11 +9,10 @@
namespace AK {
MappedFile::MappedFile(const String& file_name)
: m_file_name(file_name)
MappedFile::MappedFile(const StringView& file_name)
{
m_size = PAGE_SIZE;
m_fd = open(m_file_name.characters(), O_RDONLY | O_CLOEXEC);
m_fd = open(file_name.characters(), O_RDONLY | O_CLOEXEC);
if (m_fd != -1) {
struct stat st;
@ -26,7 +25,7 @@ MappedFile::MappedFile(const String& file_name)
}
#ifdef DEBUG_MAPPED_FILE
dbgprintf("MappedFile{%s} := { m_fd=%d, m_size=%u, m_map=%p }\n", m_file_name.characters(), m_fd, m_size, m_map);
dbgprintf("MappedFile{%s} := { m_fd=%d, m_size=%u, m_map=%p }\n", file_name.characters(), m_fd, m_size, m_map);
#endif
}
@ -44,15 +43,13 @@ void MappedFile::unmap()
ASSERT(rc == 0);
rc = close(m_fd);
ASSERT(rc == 0);
m_file_name = {};
m_size = 0;
m_fd = -1;
m_map = (void*)-1;
}
MappedFile::MappedFile(MappedFile&& other)
: m_file_name(move(other.m_file_name))
, m_size(other.m_size)
: m_size(other.m_size)
, m_fd(other.m_fd)
, m_map(other.m_map)
{
@ -66,7 +63,6 @@ MappedFile& MappedFile::operator=(MappedFile&& other)
if (this == &other)
return *this;
unmap();
swap(m_file_name, other.m_file_name);
swap(m_size, other.m_size);
swap(m_fd, other.m_fd);
swap(m_map, other.m_map);

View file

@ -1,13 +1,13 @@
#pragma once
#include "AKString.h"
#include "StringView.h"
namespace AK {
class MappedFile {
public:
MappedFile() {}
explicit MappedFile(const String& file_name);
explicit MappedFile(const StringView& file_name);
MappedFile(MappedFile&&);
~MappedFile();
@ -21,7 +21,6 @@ public:
size_t size() const { return m_size; }
private:
String m_file_name;
size_t m_size { 0 };
int m_fd { -1 };
void* m_map { (void*)-1 };

View file

@ -1,17 +1,15 @@
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;
typedef long long unsigned int qword;
#pragma once
[[gnu::always_inline]] inline size_t strlen(const char* str)
{
size_t len = 0;
while (*(str++))
++len;
return len;
}
#include <AK/Types.h>
#include <stdarg.h>
static constexpr const char* h = "0123456789abcdef";
static constexpr const char* printf_hex_digits = "0123456789abcdef";
#ifdef __serenity__
extern "C" size_t strlen(const char*);
#else
#include <string.h>
#endif
template<typename PutChFunc, typename T>
[[gnu::always_inline]] inline int print_hex(PutChFunc putch, char*& bufptr, T number, byte fields)
@ -20,7 +18,7 @@ template<typename PutChFunc, typename T>
byte shr_count = fields * 4;
while (shr_count) {
shr_count -= 4;
putch(bufptr, h[(number >> shr_count) & 0x0F]);
putch(bufptr, printf_hex_digits[(number >> shr_count) & 0x0F]);
++ret;
}
return ret;
@ -180,7 +178,7 @@ template<typename PutChFunc>
}
template<typename PutChFunc>
[[gnu::always_inline]] inline int printf_internal(PutChFunc putch, char* buffer, const char*& fmt, char*& ap)
[[gnu::always_inline]] inline int printf_internal(PutChFunc putch, char* buffer, const char*& fmt, va_list ap)
{
const char* p;

49
AK/Queue.h Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include <AK/OwnPtr.h>
#include <AK/SinglyLinkedList.h>
#include <AK/Vector.h>
namespace AK {
template<typename T>
class Queue {
public:
Queue() { }
~Queue() { }
int size() const { return m_size; }
bool is_empty() const { return m_size == 0; }
void enqueue(T&& value)
{
if (m_segments.is_empty() || m_segments.last()->size() >= segment_size)
m_segments.append(make<Vector<T, segment_size>>());
m_segments.last()->append(move(value));
++m_size;
}
T dequeue()
{
ASSERT(!is_empty());
auto value = move((*m_segments.first())[m_index_into_first++]);
if (m_index_into_first == segment_size) {
m_segments.take_first();
m_index_into_first = 0;
}
--m_size;
return value;
}
private:
static const int segment_size = 1000;
SinglyLinkedList<OwnPtr<Vector<T, segment_size>>> m_segments;
int m_index_into_first { 0 };
int m_size { 0 };
};
}
using AK::Queue;

View file

@ -1,5 +1,7 @@
#pragma once
#include <AK/StdLibExtras.h>
namespace AK {
template<typename T>

View file

@ -103,20 +103,41 @@ public:
return *this;
}
RetainPtr& operator=(T* ptr)
template<typename U>
RetainPtr& operator=(const Retained<U>& other)
{
if (m_ptr != ptr)
if (m_ptr != other.ptr())
release_if_not_null(m_ptr);
m_ptr = ptr;
m_ptr = const_cast<T*>(other.ptr());
ASSERT(m_ptr);
retain_if_not_null(m_ptr);
return *this;
}
RetainPtr& operator=(T& object)
template<typename U>
RetainPtr& operator=(const RetainPtr<U>& other)
{
if (m_ptr != other.ptr())
release_if_not_null(m_ptr);
m_ptr = const_cast<T*>(other.ptr());
retain_if_not_null(m_ptr);
return *this;
}
RetainPtr& operator=(const T* ptr)
{
if (m_ptr != ptr)
release_if_not_null(m_ptr);
m_ptr = const_cast<T*>(ptr);
retain_if_not_null(m_ptr);
return *this;
}
RetainPtr& operator=(const T& object)
{
if (m_ptr != &object)
release_if_not_null(m_ptr);
m_ptr = &object;
m_ptr = const_cast<T*>(&object);
retain_if_not_null(m_ptr);
return *this;
}

View file

@ -44,16 +44,10 @@ public:
{
m_ptr->retain();
}
RETURN_TYPESTATE(unconsumed)
Retained(T& object)
: m_ptr(&object)
{
m_ptr->retain();
}
template<typename U>
RETURN_TYPESTATE(unconsumed)
Retained(U& object)
: m_ptr(&static_cast<T&>(object))
Retained(const U& object)
: m_ptr(&const_cast<T&>(static_cast<const T&>(object)))
{
m_ptr->retain();
}
@ -200,6 +194,19 @@ public:
return m_ptr;
}
CALLABLE_WHEN(unconsumed)
operator T&()
{
ASSERT(m_ptr);
return *m_ptr;
}
CALLABLE_WHEN(unconsumed)
operator const T&() const
{
ASSERT(m_ptr);
return *m_ptr;
}
private:
Retained() {}

24
AK/ScopeGuard.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
namespace AK {
template<typename Callback>
class ScopeGuard {
public:
ScopeGuard(Callback callback)
: m_callback(move(callback))
{
}
~ScopeGuard()
{
m_callback();
}
private:
Callback m_callback;
};
}
using AK::ScopeGuard;

View file

@ -9,7 +9,7 @@ class SinglyLinkedList {
private:
struct Node {
explicit Node(T&& v)
: value(v)
: value(move(v))
{
}
T value;
@ -66,7 +66,7 @@ public:
{
ASSERT(m_head);
auto* prev_head = m_head;
T value = first();
T value = move(first());
if (m_tail == m_head)
m_tail = nullptr;
m_head = m_head->next;

View file

@ -292,4 +292,5 @@ using AK::IsSame;
using AK::max;
using AK::min;
using AK::move;
using AK::RemoveConst;
using AK::swap;

View file

@ -1,7 +1,7 @@
#include "AKString.h"
#include "StdLibExtras.h"
#include "StringBuilder.h"
#include <LibC/stdarg.h>
#include <stdarg.h>
namespace AK {
@ -68,22 +68,27 @@ StringView String::substring_view(int start, int length) const
}
Vector<String> String::split(const char separator) const
{
return split_limit(separator, 0);
}
Vector<String> String::split_limit(const char separator, int limit) const
{
if (is_empty())
return {};
Vector<String> v;
ssize_t substart = 0;
for (ssize_t i = 0; i < length(); ++i) {
int substart = 0;
for (int i = 0; i < length() && (v.size() + 1) != limit; ++i) {
char ch = characters()[i];
if (ch == separator) {
ssize_t sublen = i - substart;
int sublen = i - substart;
if (sublen != 0)
v.append(substring(substart, sublen));
substart = i + 1;
}
}
ssize_t taillen = length() - substart;
int taillen = length() - substart;
if (taillen != 0)
v.append(substring(substart, taillen));
if (characters()[length() - 1] == separator)
@ -97,21 +102,21 @@ Vector<StringView> String::split_view(const char separator) const
return {};
Vector<StringView> v;
ssize_t substart = 0;
for (ssize_t i = 0; i < length(); ++i) {
int substart = 0;
for (int i = 0; i < length(); ++i) {
char ch = characters()[i];
if (ch == separator) {
ssize_t sublen = i - substart;
int sublen = i - substart;
if (sublen != 0)
v.append(substring_view(substart, sublen));
substart = i + 1;
}
}
ssize_t taillen = length() - substart;
int taillen = length() - substart;
if (taillen != 0)
v.append(substring_view(substart, taillen));
if (characters()[length() - 1] == separator)
v.append(empty().view());
v.append(empty());
return v;
}
@ -126,7 +131,7 @@ int String::to_int(bool& ok) const
{
bool negative = false;
int value = 0;
ssize_t i = 0;
int i = 0;
if (is_null()) {
ok = false;
@ -153,7 +158,7 @@ int String::to_int(bool& ok) const
unsigned String::to_uint(bool& ok) const
{
unsigned value = 0;
for (ssize_t i = 0; i < length(); ++i) {
for (int i = 0; i < length(); ++i) {
if (characters()[i] < '0' || characters()[i] > '9') {
ok = false;
return 0;
@ -175,7 +180,18 @@ String String::format(const char* fmt, ...)
return builder.to_string();
}
bool String::ends_with(const String& str) const
bool String::starts_with(const StringView& str) const
{
if (str.is_empty())
return true;
if (is_empty())
return false;
if (str.length() > length())
return false;
return !memcmp(characters(), str.characters(), str.length());
}
bool String::ends_with(const StringView& str) const
{
if (str.is_empty())
return true;
@ -196,27 +212,28 @@ String String::repeated(char ch, int count)
return *impl;
}
bool String::matches(const String& mask, CaseSensitivity case_sensitivity) const
bool String::matches(const StringView& mask, CaseSensitivity case_sensitivity) const
{
if (case_sensitivity == CaseSensitivity::CaseInsensitive) {
String this_lower = this->to_lowercase();
String mask_lower = mask.to_lowercase();
String mask_lower = String(mask).to_lowercase();
return this_lower.match_helper(mask_lower);
}
return match_helper(mask);
}
bool String::match_helper(const String& mask) const
bool String::match_helper(const StringView& mask) const
{
if (is_null() || mask.is_null())
if (is_null())
return false;
const char* string_ptr = characters();
const char* mask_ptr = mask.characters();
const char* mask_end = mask_ptr + mask.length();
// Match string against mask directly unless we hit a *
while ((*string_ptr) && (*mask_ptr != '*')) {
while ((*string_ptr) && (mask_ptr < mask_end) && (*mask_ptr != '*')) {
if ((*mask_ptr != *string_ptr) && (*mask_ptr != '?'))
return false;
mask_ptr++;
@ -227,27 +244,29 @@ bool String::match_helper(const String& mask) const
const char* mp = nullptr;
while (*string_ptr) {
if (*mask_ptr == '*') {
if ((mask_ptr < mask_end) && (*mask_ptr == '*')) {
// If we have only a * left, there is no way to not match.
if (!*++mask_ptr)
if (++mask_ptr == mask_end)
return true;
mp = mask_ptr;
cp = string_ptr + 1;
} else if ((*mask_ptr == *string_ptr) || (*mask_ptr == '?')) {
} else if ((mask_ptr < mask_end) && ((*mask_ptr == *string_ptr) || (*mask_ptr == '?'))) {
mask_ptr++;
string_ptr++;
} else {
} else if ((cp != nullptr) && (mp != nullptr)) {
mask_ptr = mp;
string_ptr = cp++;
} else {
break;
}
}
// Handle any trailing mask
while (*mask_ptr == '*')
while ((mask_ptr < mask_end) && (*mask_ptr == '*'))
mask_ptr++;
// If we 'ate' all of the mask then we match.
return !*mask_ptr;
// If we 'ate' all of the mask and the string then we match.
return (mask_ptr == mask_end) && !*string_ptr;
}
}

View file

@ -1,22 +1,22 @@
#include "StringBuilder.h"
#include "printf.cpp"
#include <AK/PrintfImplementation.h>
#include <AK/StdLibExtras.h>
#include <LibC/stdarg.h>
#include <AK/StringBuilder.h>
#include <stdarg.h>
namespace AK {
inline void StringBuilder::will_append(ssize_t size)
inline void StringBuilder::will_append(int size)
{
if ((m_length + size) > m_buffer.size())
m_buffer.grow(max((ssize_t)16, m_buffer.size() * 2 + size));
m_buffer.grow(max((int)16, m_buffer.size() * 2 + size));
}
StringBuilder::StringBuilder(ssize_t initial_capacity)
StringBuilder::StringBuilder(int initial_capacity)
{
m_buffer.grow(initial_capacity);
}
void StringBuilder::append(const String& str)
void StringBuilder::append(const StringView& str)
{
if (str.is_empty())
return;
@ -25,7 +25,7 @@ void StringBuilder::append(const String& str)
m_length += str.length();
}
void StringBuilder::append(const char* characters, ssize_t length)
void StringBuilder::append(const char* characters, int length)
{
if (!length)
return;

View file

@ -2,18 +2,18 @@
#include "AKString.h"
#include "Vector.h"
#include <LibC/stdarg.h>
#include <stdarg.h>
namespace AK {
class StringBuilder {
public:
explicit StringBuilder(ssize_t initial_capacity = 16);
explicit StringBuilder(int initial_capacity = 16);
~StringBuilder() {}
void append(const String&);
void append(const StringView&);
void append(char);
void append(const char*, ssize_t);
void append(const char*, int);
void appendf(const char*, ...);
void appendvf(const char*, va_list);
@ -21,10 +21,10 @@ public:
ByteBuffer to_byte_buffer();
private:
void will_append(ssize_t);
void will_append(int);
ByteBuffer m_buffer;
ssize_t m_length { 0 };
int m_length { 0 };
};
}

View file

@ -3,6 +3,10 @@
#include "StdLibExtras.h"
#include "kmalloc.h"
#ifndef __serenity__
#include <new>
#endif
//#define DEBUG_STRINGIMPL
#ifdef DEBUG_STRINGIMPL

View file

@ -3,6 +3,13 @@
namespace AK {
StringView::StringView(const String& string)
: m_impl(string.impl())
, m_characters(string.characters())
, m_length(string.length())
{
}
Vector<StringView> StringView::split_view(const char separator) const
{
if (is_empty())
@ -23,7 +30,7 @@ Vector<StringView> StringView::split_view(const char separator) const
if (taillen != 0)
v.append(substring_view(substart, taillen));
if (characters()[length() - 1] == separator)
v.append(String::empty().view());
v.append(String::empty());
return v;
}
@ -35,6 +42,24 @@ StringView StringView::substring_view(int start, int length) const
return { m_characters + start, length };
}
StringView StringView::substring_view_starting_from_substring(const StringView& substring) const
{
const char* remaining_characters = substring.characters();
ASSERT(remaining_characters >= m_characters);
ASSERT(remaining_characters <= m_characters + m_length);
int remaining_length = m_length - (remaining_characters - m_characters);
return { remaining_characters, remaining_length };
}
StringView StringView::substring_view_starting_after_substring(const StringView& substring) const
{
const char* remaining_characters = substring.characters() + substring.length();
ASSERT(remaining_characters >= m_characters);
ASSERT(remaining_characters <= m_characters + m_length);
int remaining_length = m_length - (remaining_characters - m_characters);
return { remaining_characters, remaining_length };
}
unsigned StringView::to_uint(bool& ok) const
{
unsigned value = 0;

View file

@ -5,6 +5,7 @@
namespace AK {
class String;
class StringImpl;
class StringView {
public:
@ -27,7 +28,9 @@ public:
++m_length;
}
}
StringView(const String& string);
bool is_null() const { return !m_characters; }
bool is_empty() const { return m_length == 0; }
const char* characters() const { return m_characters; }
int length() const { return m_length; }
@ -37,12 +40,46 @@ public:
Vector<StringView> split_view(char) const;
unsigned to_uint(bool& ok) const;
bool operator==(const char* cstring) const { return !strcmp(m_characters, cstring); }
bool operator!=(const char* cstring) const { return strcmp(m_characters, cstring); }
// Create a new substring view of this string view, starting either at the beginning of
// the given substring view, or after its end, and continuing until the end of this string
// view (that is, for the remaining part of its length). For example,
//
// StringView str { "foobar" };
// StringView substr = str.substring_view(1, 2); // "oo"
// StringView substr_from = str.substring_view_starting_from_substring(subst); // "oobar"
// StringView substr_after = str.substring_view_starting_after_substring(subst); // "bar"
//
// Note that this only works if the string view passed as an argument is indeed a substring
// view of this string view, such as one created by substring_view() and split_view(). It
// does not work for arbitrary strings; for example declaring substr in the example above as
//
// StringView substr { "oo" };
//
// would not work.
StringView substring_view_starting_from_substring(const StringView& substring) const;
StringView substring_view_starting_after_substring(const StringView& substring) const;
bool operator==(const char* cstring) const
{
if (is_null())
return !cstring;
if (!cstring)
return false;
int other_length = strlen(cstring);
if (m_length != other_length)
return false;
return !memcmp(m_characters, cstring, m_length);
}
bool operator!=(const char* cstring) const
{
return !(*this == cstring);
}
bool operator==(const String&) const;
private:
friend class String;
const StringImpl* m_impl { nullptr };
const char* m_characters { nullptr };
int m_length { 0 };
};

2
AK/Tests/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
TestString
TestQueue

12
AK/Tests/Makefile Normal file
View file

@ -0,0 +1,12 @@
all: TestString TestQueue
CXXFLAGS = -std=c++17 -Wall -Wextra
TestString: TestString.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp TestHelpers.h
$(CXX) $(CXXFLAGS) -I../ -I../../ -o $@ TestString.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp
TestQueue: TestQueue.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp TestHelpers.h
$(CXX) $(CXXFLAGS) -I../ -I../../ -o $@ TestQueue.cpp ../String.cpp ../StringImpl.cpp ../StringBuilder.cpp ../StringView.cpp
clean:
rm -f TestString TestQueue

68
AK/Tests/TestHelpers.h Normal file
View file

@ -0,0 +1,68 @@
#pragma once
#include <stdio.h>
#include <AK/AKString.h>
#define LOG_FAIL(cond) \
fprintf(stderr, "\033[31;1mFAIL\033[0m: " #cond "\n")
#define LOG_PASS(cond) \
fprintf(stderr, "\033[32;1mPASS\033[0m: " #cond "\n")
#define LOG_FAIL_EQ(cond, expected_value, actual_value) \
fprintf(stderr, "\033[31;1mFAIL\033[0m: " #cond " should be " #expected_value ", got "); \
stringify_for_test(actual_value); \
fprintf(stderr, "\n")
#define LOG_PASS_EQ(cond, expected_value) \
fprintf(stderr, "\033[32;1mPASS\033[0m: " #cond " should be " #expected_value " and it is\n")
#define EXPECT_EQ(expr, expected_value) \
do { \
auto result = (expr); \
if (!(result == expected_value)) { \
LOG_FAIL_EQ(expr, expected_value, result); \
} else { \
LOG_PASS_EQ(expr, expected_value); \
} \
} while(0)
#define EXPECT(cond) \
do { \
if (!(cond)) { \
LOG_FAIL(cond); \
} else { \
LOG_PASS(cond); \
} \
} while(0)
inline void stringify_for_test(int value)
{
fprintf(stderr, "%d", value);
}
inline void stringify_for_test(unsigned value)
{
fprintf(stderr, "%u", value);
}
inline void stringify_for_test(const char* value)
{
fprintf(stderr, "%s", value);
}
inline void stringify_for_test(char value)
{
fprintf(stderr, "%c", value);
}
inline void stringify_for_test(const AK::String& string)
{
stringify_for_test(string.characters());
}
inline void stringify_for_test(const AK::StringImpl& string)
{
stringify_for_test(string.characters());
}

43
AK/Tests/TestQueue.cpp Normal file
View file

@ -0,0 +1,43 @@
#include "TestHelpers.h"
#include <AK/AKString.h>
#include <AK/Queue.h>
int main()
{
EXPECT(Queue<int>().is_empty());
EXPECT(Queue<int>().size() == 0);
Queue<int> ints;
ints.enqueue(1);
ints.enqueue(2);
ints.enqueue(3);
EXPECT_EQ(ints.size(), 3);
EXPECT_EQ(ints.dequeue(), 1);
EXPECT_EQ(ints.size(), 2);
EXPECT_EQ(ints.dequeue(), 2);
EXPECT_EQ(ints.size(), 1);
EXPECT_EQ(ints.dequeue(), 3);
EXPECT_EQ(ints.size(), 0);
Queue<String> strings;
strings.enqueue("ABC");
strings.enqueue("DEF");
EXPECT_EQ(strings.size(), 2);
EXPECT_EQ(strings.dequeue(), "ABC");
EXPECT_EQ(strings.dequeue(), "DEF");
EXPECT(strings.is_empty());
for (int i = 0; i < 10000; ++i) {
strings.enqueue(String::format("%d", i));
EXPECT_EQ(strings.size(), i + 1);
}
for (int i = 0; i < 10000; ++i) {
bool ok;
EXPECT_EQ(strings.dequeue().to_int(ok), i);
}
EXPECT(strings.is_empty());
return 0;
}

59
AK/Tests/TestString.cpp Normal file
View file

@ -0,0 +1,59 @@
#include "TestHelpers.h"
#include <AK/AKString.h>
int main()
{
EXPECT(String().is_null());
EXPECT(String().is_empty());
EXPECT(!String().characters());
EXPECT(!String("").is_null());
EXPECT(String("").is_empty());
EXPECT(String("").characters());
EXPECT(String("").impl() == String::empty().impl());
String test_string = "ABCDEF";
EXPECT(!test_string.is_empty());
EXPECT(!test_string.is_null());
EXPECT_EQ(test_string.length(), 6);
EXPECT_EQ(test_string.length(), (int)strlen(test_string.characters()));
EXPECT(test_string.characters());
EXPECT(!strcmp(test_string.characters(), "ABCDEF"));
EXPECT(test_string == "ABCDEF");
EXPECT(test_string != "ABCDE");
EXPECT(test_string != "ABCDEFG");
EXPECT_EQ(test_string[0], 'A');
EXPECT_EQ(test_string[1], 'B');
EXPECT(test_string.starts_with("AB"));
EXPECT(test_string.starts_with("ABCDEF"));
EXPECT(!test_string.starts_with("DEF"));
EXPECT(test_string.ends_with("EF"));
EXPECT(test_string.ends_with("ABCDEF"));
EXPECT(!test_string.ends_with("ABC"));
auto test_string_copy = test_string;
EXPECT_EQ(test_string, test_string_copy);
EXPECT_EQ(test_string.characters(), test_string_copy.characters());
auto test_string_move = move(test_string_copy);
EXPECT_EQ(test_string, test_string_move);
EXPECT(test_string_copy.is_null());
EXPECT_EQ(String::repeated('x', 0), "");
EXPECT_EQ(String::repeated('x', 1), "x");
EXPECT_EQ(String::repeated('x', 2), "xx");
bool ok;
EXPECT(String("123").to_int(ok) == 123 && ok);
EXPECT(String("-123").to_int(ok) == -123 && ok);
EXPECT(String("ABC").to_lowercase() == "abc");
EXPECT(String("AbC").to_uppercase() == "ABC");
return 0;
}

View file

@ -3,25 +3,28 @@
namespace AK {
template<typename TimevalType>
inline void timeval_sub(const TimevalType* a, const TimevalType* b, TimevalType* result)
inline void timeval_sub(const TimevalType& a, const TimevalType& b, TimevalType& result)
{
result->tv_sec = a->tv_sec - b->tv_sec;
result->tv_usec = a->tv_usec - b->tv_usec;
if (result->tv_usec < 0) {
--result->tv_sec;
result->tv_usec += 1000000;
result.tv_sec = a.tv_sec - b.tv_sec;
result.tv_usec = a.tv_usec - b.tv_usec;
if (result.tv_usec < 0) {
--result.tv_sec;
result.tv_usec += 1000000;
}
}
template<typename TimevalType>
inline void timeval_add(const TimevalType* a, const TimevalType* b, TimevalType* result)
inline void timeval_add(const TimevalType& a, const TimevalType& b, TimevalType& result)
{
result->tv_sec = a->tv_sec + b->tv_sec;
result->tv_usec = a->tv_usec + b->tv_usec;
if (result->tv_usec > 1000000) {
++result->tv_sec;
result->tv_usec -= 1000000;
result.tv_sec = a.tv_sec + b.tv_sec;
result.tv_usec = a.tv_usec + b.tv_usec;
if (result.tv_usec > 1000000) {
++result.tv_sec;
result.tv_usec -= 1000000;
}
}
}
using AK::timeval_add;
using AK::timeval_sub;

View file

@ -1,5 +1,7 @@
#pragma once
#include <AK/IterationDecision.h>
#ifdef __serenity__
typedef unsigned char byte;
typedef unsigned short word;
@ -48,11 +50,6 @@ constexpr unsigned KB = 1024;
constexpr unsigned MB = KB * KB;
constexpr unsigned GB = KB * KB * KB;
enum class IterationDecision {
Continue,
Abort
};
namespace std {
typedef decltype(nullptr) nullptr_t;
}

26
AK/ValueRestorer.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
namespace AK {
template<typename T>
class ValueRestorer {
public:
ValueRestorer(T& variable)
: m_variable(variable)
, m_saved_value(variable)
{
}
~ValueRestorer()
{
m_variable = m_saved_value;
}
private:
T& m_variable;
T m_saved_value;
};
}
using AK::ValueRestorer;

View file

@ -4,6 +4,10 @@
#include <AK/StdLibExtras.h>
#include <AK/kmalloc.h>
#ifndef __serenity__
#include <new>
#endif
namespace AK {
template<typename T, int inline_capacity = 0>
@ -294,6 +298,19 @@ public:
m_capacity = new_capacity;
}
void shift_left(int count)
{
ASSERT(count <= m_size);
if (count == m_size) {
clear();
return;
}
for (int i = 0; i < m_size - count; ++i) {
at(i) = move(at(i + count));
}
m_size -= count;
}
void resize(int new_size)
{
if (new_size == size())

View file

@ -1,10 +1,9 @@
#include <LibGUI/GApplication.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GDesktop.h>
#include <stdio.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GWindow.h>
#include <sys/utsname.h>
int main(int argc, char** argv)
@ -50,7 +49,7 @@ int main(int argc, char** argv)
quit_button->set_text("Okay");
quit_button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
quit_button->set_preferred_size({ 100, 20 });
quit_button->on_click = [] (GButton&) {
quit_button->on_click = [](GButton&) {
GApplication::the().quit(0);
};

View file

@ -1,7 +1,7 @@
#include <LibGUI/GApplication.h>
#include <LibCore/CHttpRequest.h>
#include <LibCore/CHttpResponse.h>
#include <LibCore/CNetworkJob.h>
#include <LibGUI/GApplication.h>
#include <stdio.h>
int main(int argc, char** argv)
@ -13,7 +13,7 @@ int main(int argc, char** argv)
request.set_path("/");
auto job = request.schedule();
job->on_finish = [&job] (bool success) {
job->on_finish = [&job](bool success) {
if (!success) {
dbgprintf("on_finish: request failed :(\n");
return;

View file

@ -1,8 +1,8 @@
#include "DirectoryView.h"
#include <LibGUI/GSortingProxyModel.h>
#include <AK/FileSystemPath.h>
#include <unistd.h>
#include <LibGUI/GSortingProxyModel.h>
#include <stdio.h>
#include <unistd.h>
void DirectoryView::handle_activation(const GModelIndex& index)
{
@ -58,28 +58,28 @@ DirectoryView::DirectoryView(GWidget* parent)
m_item_view->set_model_column(GDirectoryModel::Column::Name);
m_item_view->on_model_notification = [this] (const GModelNotification& notification) {
m_item_view->on_model_notification = [this](const GModelNotification& notification) {
if (notification.type() == GModelNotification::Type::ModelUpdated) {
set_status_message(String::format("%d item%s (%u byte%s)",
model().row_count(),
model().row_count() != 1 ? "s" : "",
model().bytes_in_files(),
model().bytes_in_files() != 1 ? "s" : ""));
model().row_count(),
model().row_count() != 1 ? "s" : "",
model().bytes_in_files(),
model().bytes_in_files() != 1 ? "s" : ""));
if (on_path_change)
on_path_change(model().path());
}
};
m_model->on_thumbnail_progress = [this] (int done, int total) {
m_model->on_thumbnail_progress = [this](int done, int total) {
if (on_thumbnail_progress)
on_thumbnail_progress(done, total);
};
m_item_view->on_activation = [&] (const GModelIndex& index) {
m_item_view->on_activation = [&](const GModelIndex& index) {
handle_activation(index);
};
m_table_view->on_activation = [&] (auto& index) {
m_table_view->on_activation = [&](auto& index) {
auto& filter_model = (GSortingProxyModel&)*m_table_view->model();
handle_activation(filter_model.map_to_target(index));
};
@ -108,7 +108,7 @@ void DirectoryView::set_view_mode(ViewMode mode)
ASSERT_NOT_REACHED();
}
void DirectoryView::add_path_to_history(const String& path)
void DirectoryView::add_path_to_history(const StringView& path)
{
if (m_path_history_position < m_path_history.size())
m_path_history.resize(m_path_history_position + 1);
@ -117,13 +117,13 @@ void DirectoryView::add_path_to_history(const String& path)
m_path_history_position = m_path_history.size() - 1;
}
void DirectoryView::open(const String& path)
void DirectoryView::open(const StringView& path)
{
add_path_to_history(path);
model().open(path);
}
void DirectoryView::set_status_message(const String& message)
void DirectoryView::set_status_message(const StringView& message)
{
if (on_status_message)
on_status_message(message);

View file

@ -12,7 +12,7 @@ public:
explicit DirectoryView(GWidget* parent);
virtual ~DirectoryView() override;
void open(const String& path);
void open(const StringView& path);
String path() const { return model().path(); }
void open_parent_directory();
void open_previous_directory();
@ -22,12 +22,11 @@ public:
void refresh();
Function<void(const String&)> on_path_change;
Function<void(String)> on_status_message;
Function<void(const StringView&)> on_path_change;
Function<void(const StringView&)> on_status_message;
Function<void(int done, int total)> on_thumbnail_progress;
enum ViewMode
{
enum ViewMode {
Invalid,
List,
Icon
@ -41,14 +40,14 @@ private:
void handle_activation(const GModelIndex&);
void set_status_message(const String&);
void set_status_message(const StringView&);
ViewMode m_view_mode { Invalid };
Retained<GDirectoryModel> m_model;
int m_path_history_position { 0 };
Vector<String> m_path_history;
void add_path_to_history(const String& path);
void add_path_to_history(const StringView& path);
GTableView* m_table_view { nullptr };
GItemView* m_item_view { nullptr };

View file

@ -1,25 +1,25 @@
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GBoxLayout.h>
#include "DirectoryView.h"
#include <AK/FileSystemPath.h>
#include <LibCore/CUserInfo.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GFileSystemModel.h>
#include <LibGUI/GInputBox.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GMessageBox.h>
#include <LibGUI/GProgressBar.h>
#include <LibGUI/GSplitter.h>
#include <LibGUI/GStatusBar.h>
#include <LibGUI/GTextEditor.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GInputBox.h>
#include <LibGUI/GMessageBox.h>
#include <LibGUI/GProgressBar.h>
#include <LibGUI/GTreeView.h>
#include <LibGUI/GFileSystemModel.h>
#include <LibGUI/GSplitter.h>
#include <LibCore/CUserInfo.h>
#include <AK/FileSystemPath.h>
#include <unistd.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <signal.h>
#include <stdio.h>
#include "DirectoryView.h"
#include <unistd.h>
int main(int argc, char** argv)
{
@ -76,24 +76,24 @@ int main(int argc, char** argv)
directory_view->open(location_textbox->text());
};
file_system_model->on_selection_changed = [&] (auto& index) {
file_system_model->on_selection_changed = [&](auto& index) {
auto path = file_system_model->path(index);
if (directory_view->path() == path)
return;
directory_view->open(path);
};
auto open_parent_directory_action = GAction::create("Open parent directory", { Mod_Alt, Key_Up }, GraphicsBitmap::load_from_file("/res/icons/16x16/open-parent-directory.png"), [directory_view] (const GAction&) {
auto open_parent_directory_action = GAction::create("Open parent directory", { Mod_Alt, Key_Up }, GraphicsBitmap::load_from_file("/res/icons/16x16/open-parent-directory.png"), [directory_view](const GAction&) {
directory_view->open_parent_directory();
});
auto mkdir_action = GAction::create("New directory...", GraphicsBitmap::load_from_file("/res/icons/16x16/mkdir.png"), [&] (const GAction&) {
auto mkdir_action = GAction::create("New directory...", GraphicsBitmap::load_from_file("/res/icons/16x16/mkdir.png"), [&](const GAction&) {
GInputBox input_box("Enter name:", "New directory", window);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty()) {
auto new_dir_path = FileSystemPath(String::format("%s/%s",
directory_view->path().characters(),
input_box.text_value().characters()
)).string();
directory_view->path().characters(),
input_box.text_value().characters()))
.string();
int rc = mkdir(new_dir_path.characters(), 0777);
if (rc < 0) {
GMessageBox::show(String::format("mkdir(\"%s\") failed: %s", new_dir_path.characters(), strerror(errno)), "Error", GMessageBox::Type::Error, window);
@ -106,7 +106,7 @@ int main(int argc, char** argv)
RetainPtr<GAction> view_as_table_action;
RetainPtr<GAction> view_as_icons_action;
view_as_table_action = GAction::create("Table view", { Mod_Ctrl, KeyCode::Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/table-view.png"), [&] (const GAction&) {
view_as_table_action = GAction::create("Table view", { Mod_Ctrl, KeyCode::Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/table-view.png"), [&](const GAction&) {
directory_view->set_view_mode(DirectoryView::ViewMode::List);
view_as_icons_action->set_checked(false);
view_as_table_action->set_checked(true);
@ -114,7 +114,7 @@ int main(int argc, char** argv)
view_as_table_action->set_checkable(true);
view_as_table_action->set_checked(false);
view_as_icons_action = GAction::create("Icon view", { Mod_Ctrl, KeyCode::Key_I }, GraphicsBitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&] (const GAction&) {
view_as_icons_action = GAction::create("Icon view", { Mod_Ctrl, KeyCode::Key_I }, GraphicsBitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GAction&) {
directory_view->set_view_mode(DirectoryView::ViewMode::Icon);
view_as_table_action->set_checked(false);
view_as_icons_action->set_checked(true);
@ -122,20 +122,20 @@ int main(int argc, char** argv)
view_as_icons_action->set_checkable(true);
view_as_icons_action->set_checked(true);
auto copy_action = GAction::create("Copy", GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), [] (const GAction&) {
auto copy_action = GAction::create("Copy", GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), [](const GAction&) {
dbgprintf("'Copy' action activated!\n");
});
auto delete_action = GAction::create("Delete", GraphicsBitmap::load_from_file("/res/icons/16x16/delete.png"), [] (const GAction&) {
auto delete_action = GAction::create("Delete", GraphicsBitmap::load_from_file("/res/icons/16x16/delete.png"), [](const GAction&) {
dbgprintf("'Delete' action activated!\n");
});
auto go_back_action = GAction::create("Go Back", GraphicsBitmap::load_from_file("/res/icons/16x16/go-back.png"), [directory_view] (const GAction&) {
auto go_back_action = GAction::create("Go Back", GraphicsBitmap::load_from_file("/res/icons/16x16/go-back.png"), [directory_view](const GAction&) {
dbgprintf("'Go Back' action activated!\n");
directory_view->open_previous_directory();
});
auto go_forward_action = GAction::create("Go Forward", GraphicsBitmap::load_from_file("/res/icons/16x16/go-forward.png"), [directory_view] (const GAction&) {
auto go_forward_action = GAction::create("Go Forward", GraphicsBitmap::load_from_file("/res/icons/16x16/go-forward.png"), [directory_view](const GAction&) {
dbgprintf("'Go Forward' action activated!\n");
directory_view->open_next_directory();
});
@ -143,7 +143,7 @@ int main(int argc, char** argv)
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("File Manager");
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [] (const GAction&) {
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [](const GAction&) {
GApplication::the().quit(0);
return;
}));
@ -167,7 +167,7 @@ int main(int argc, char** argv)
menubar->add_menu(move(go_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(GAction::create("About", [] (const GAction&) {
help_menu->add_action(GAction::create("About", [](const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));
@ -187,7 +187,7 @@ int main(int argc, char** argv)
main_toolbar->add_action(*view_as_icons_action);
main_toolbar->add_action(*view_as_table_action);
directory_view->on_path_change = [window, location_textbox, &file_system_model, tree_view, &go_forward_action, &go_back_action, directory_view] (const String& new_path) {
directory_view->on_path_change = [window, location_textbox, &file_system_model, tree_view, &go_forward_action, &go_back_action, directory_view](const String& new_path) {
window->set_title(String::format("File Manager: %s", new_path.characters()));
location_textbox->set_text(new_path);
file_system_model->set_selected_index(file_system_model->index(new_path));
@ -195,15 +195,15 @@ int main(int argc, char** argv)
tree_view->update();
go_forward_action->set_enabled(directory_view->path_history_position()
< directory_view->path_history_size() - 1);
< directory_view->path_history_size() - 1);
go_back_action->set_enabled(directory_view->path_history_position() > 0);
};
directory_view->on_status_message = [statusbar] (String message) {
statusbar->set_text(move(message));
directory_view->on_status_message = [statusbar](const StringView& message) {
statusbar->set_text(message);
};
directory_view->on_thumbnail_progress = [&] (int done, int total) {
directory_view->on_thumbnail_progress = [&](int done, int total) {
if (done == total) {
progressbar->set_visible(false);
return;

View file

@ -1,13 +1,13 @@
#include "FontEditor.h"
#include "GlyphMapWidget.h"
#include "GlyphEditorWidget.h"
#include <LibGUI/GPainter.h>
#include "GlyphMapWidget.h"
#include <LibGUI/GButton.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GTextBox.h>
#include <LibGUI/GCheckBox.h>
#include <LibGUI/GSpinBox.h>
#include <LibGUI/GGroupBox.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GPainter.h>
#include <LibGUI/GSpinBox.h>
#include <LibGUI/GTextBox.h>
#include <stdlib.h>
FontEditorWidget::FontEditorWidget(const String& path, RetainPtr<Font>&& edited_font, GWidget* parent)
@ -52,7 +52,7 @@ FontEditorWidget::FontEditorWidget(const String& path, RetainPtr<Font>&& edited_
auto* save_button = new GButton(this);
save_button->set_text("Save");
save_button->set_relative_rect({ 5, 300, 105, 20 });
save_button->on_click = [this] (GButton&) {
save_button->on_click = [this](GButton&) {
dbgprintf("write to file: '%s'\n", m_path.characters());
m_edited_font->write_to_file(m_path);
};
@ -60,7 +60,7 @@ FontEditorWidget::FontEditorWidget(const String& path, RetainPtr<Font>&& edited_
auto* quit_button = new GButton(this);
quit_button->set_text("Quit");
quit_button->set_relative_rect({ 110, 300, 105, 20 });
quit_button->on_click = [] (GButton&) {
quit_button->on_click = [](GButton&) {
exit(0);
};
@ -91,25 +91,25 @@ FontEditorWidget::FontEditorWidget(const String& path, RetainPtr<Font>&& edited_
demo_label_2->update();
};
m_glyph_editor_widget->on_glyph_altered = [this, update_demo] (byte glyph) {
m_glyph_editor_widget->on_glyph_altered = [this, update_demo](byte glyph) {
m_glyph_map_widget->update_glyph(glyph);
update_demo();
};
m_glyph_map_widget->on_glyph_selected = [this, info_label, width_spinbox] (byte glyph) {
m_glyph_map_widget->on_glyph_selected = [this, info_label, width_spinbox](byte glyph) {
m_glyph_editor_widget->set_glyph(glyph);
width_spinbox->set_value(m_edited_font->glyph_width(m_glyph_map_widget->selected_glyph()));
info_label->set_text(String::format("0x%b (%c)", glyph, glyph));
};
fixed_width_checkbox->on_checked = [this, width_spinbox, update_demo] (bool checked) {
fixed_width_checkbox->on_checked = [this, width_spinbox, update_demo](bool checked) {
m_edited_font->set_fixed_width(checked);
width_spinbox->set_value(m_edited_font->glyph_width(m_glyph_map_widget->selected_glyph()));
m_glyph_editor_widget->update();
update_demo();
};
width_spinbox->on_change = [this, update_demo] (int value) {
width_spinbox->on_change = [this, update_demo](int value) {
m_edited_font->set_glyph_width(m_glyph_map_widget->selected_glyph(), value);
m_glyph_editor_widget->update();
m_glyph_map_widget->update_glyph(m_glyph_map_widget->selected_glyph());

View file

@ -44,7 +44,8 @@ Rect GlyphMapWidget::get_outer_rect(byte glyph) const
row * (font().glyph_height() + m_vertical_spacing) + 1,
font().max_glyph_width() + m_horizontal_spacing,
font().glyph_height() + m_horizontal_spacing
}.translated(frame_thickness(), frame_thickness());
}
.translated(frame_thickness(), frame_thickness());
}
void GlyphMapWidget::update_glyph(byte glyph)
@ -71,8 +72,7 @@ void GlyphMapWidget::paint_event(GPaintEvent& event)
outer_rect.x() + m_horizontal_spacing / 2,
outer_rect.y() + m_vertical_spacing / 2,
font().max_glyph_width(),
font().glyph_height()
);
font().glyph_height());
if (glyph == m_selected_glyph) {
painter.fill_rect(outer_rect, Color::from_rgb(0x84351a));
painter.draw_glyph(inner_rect.location(), glyph, Color::White);

View file

@ -1,16 +1,16 @@
#include "IRCAppWindow.h"
#include "IRCWindow.h"
#include "IRCWindowListModel.h"
#include <LibGUI/GApplication.h>
#include <LibGUI/GStackWidget.h>
#include <LibGUI/GTableView.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GInputBox.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GInputBox.h>
#include <LibGUI/GSplitter.h>
#include <LibGUI/GStackWidget.h>
#include <LibGUI/GTableView.h>
#include <LibGUI/GToolBar.h>
#include <stdio.h>
#include <stdlib.h>
@ -36,7 +36,7 @@ void IRCAppWindow::update_title()
void IRCAppWindow::setup_client()
{
m_client.aid_create_window = [this] (void* owner, IRCWindow::Type type, const String& name) {
m_client.aid_create_window = [this](void* owner, IRCWindow::Type type, const String& name) {
return &create_window(owner, type, name);
};
m_client.aid_get_active_window = [this] {
@ -45,7 +45,7 @@ void IRCAppWindow::setup_client()
m_client.aid_update_window_list = [this] {
m_window_list->model()->update();
};
m_client.on_nickname_changed = [this] (const String&) {
m_client.on_nickname_changed = [this](const String&) {
update_title();
};
@ -64,33 +64,33 @@ void IRCAppWindow::setup_client()
void IRCAppWindow::setup_actions()
{
m_join_action = GAction::create("Join channel", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-join.png"), [&] (auto&) {
m_join_action = GAction::create("Join channel", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-join.png"), [&](auto&) {
GInputBox input_box("Enter channel name:", "Join channel", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_join_action(input_box.text_value());
});
m_part_action = GAction::create("Part from channel", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-part.png"), [] (auto&) {
m_part_action = GAction::create("Part from channel", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-part.png"), [](auto&) {
printf("FIXME: Implement part action\n");
});
m_whois_action = GAction::create("Whois user", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-whois.png"), [&] (auto&) {
m_whois_action = GAction::create("Whois user", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-whois.png"), [&](auto&) {
GInputBox input_box("Enter nickname:", "IRC WHOIS lookup", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_whois_action(input_box.text_value());
});
m_open_query_action = GAction::create("Open query", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-open-query.png"), [&] (auto&) {
m_open_query_action = GAction::create("Open query", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-open-query.png"), [&](auto&) {
GInputBox input_box("Enter nickname:", "Open IRC query with...", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_open_query_action(input_box.text_value());
});
m_close_query_action = GAction::create("Close query", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-close-query.png"), [] (auto&) {
m_close_query_action = GAction::create("Close query", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-close-query.png"), [](auto&) {
printf("FIXME: Implement close-query action\n");
});
m_change_nick_action = GAction::create("Change nickname", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-nick.png"), [this] (auto&) {
m_change_nick_action = GAction::create("Change nickname", GraphicsBitmap::load_from_file("/res/icons/16x16/irc-nick.png"), [this](auto&) {
GInputBox input_box("Enter nickname:", "Change nickname", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_change_nick_action(input_box.text_value());
@ -101,7 +101,7 @@ void IRCAppWindow::setup_menus()
{
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("IRC Client");
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [] (const GAction&) {
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [](const GAction&) {
dbgprintf("Terminal: Quit menu activated!\n");
GApplication::the().quit(0);
return;
@ -120,7 +120,7 @@ void IRCAppWindow::setup_menus()
menubar->add_menu(move(server_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(GAction::create("About", [] (const GAction&) {
help_menu->add_action(GAction::create("About", [](const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));
@ -156,7 +156,7 @@ void IRCAppWindow::setup_widgets()
m_window_list->set_activates_on_selection(true);
m_window_list->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
m_window_list->set_preferred_size({ 100, 0 });
m_window_list->on_activation = [this] (auto& index) {
m_window_list->on_activation = [this](auto& index) {
auto& window = m_client.window_at(index.row());
m_container->set_active_widget(&window);
window.clear_unread_count();

View file

@ -1,6 +1,6 @@
#include "IRCChannel.h"
#include "IRCClient.h"
#include "IRCChannelMemberListModel.h"
#include "IRCClient.h"
#include <stdio.h>
#include <time.h>
@ -37,7 +37,7 @@ void IRCChannel::add_member(const String& name, char prefix)
void IRCChannel::remove_member(const String& name)
{
m_members.remove_first_matching([&] (auto& member) { return name == member.name; });
m_members.remove_first_matching([&](auto& member) { return name == member.name; });
}
void IRCChannel::add_message(char prefix, const String& name, const String& text, Color color)

View file

@ -25,7 +25,8 @@ int IRCChannelMemberListModel::column_count(const GModelIndex&) const
String IRCChannelMemberListModel::column_name(int column) const
{
switch (column) {
case Column::Name: return "Name";
case Column::Name:
return "Name";
}
ASSERT_NOT_REACHED();
}
@ -33,7 +34,8 @@ String IRCChannelMemberListModel::column_name(int column) const
GModel::ColumnMetadata IRCChannelMemberListModel::column_metadata(int column) const
{
switch (column) {
case Column::Name: return { 70, TextAlignment::CenterLeft };
case Column::Name:
return { 70, TextAlignment::CenterLeft };
}
ASSERT_NOT_REACHED();
}
@ -42,10 +44,11 @@ GVariant IRCChannelMemberListModel::data(const GModelIndex& index, Role role) co
{
if (role == Role::Display) {
switch (index.column()) {
case Column::Name: return m_channel.member_at(index.row());
case Column::Name:
return m_channel.member_at(index.row());
}
}
return { };
return {};
}
void IRCChannelMemberListModel::update()

View file

@ -7,8 +7,7 @@ class IRCChannel;
class IRCChannelMemberListModel final : public GModel {
public:
enum Column
{
enum Column {
Name
};
static Retained<IRCChannelMemberListModel> create(IRCChannel& channel) { return adopt(*new IRCChannelMemberListModel(channel)); }

View file

@ -1,16 +1,16 @@
#include "IRCClient.h"
#include "IRCChannel.h"
#include "IRCQuery.h"
#include "IRCLogBuffer.h"
#include "IRCQuery.h"
#include "IRCWindow.h"
#include "IRCWindowListModel.h"
#include <LibCore/CNotifier.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#define IRC_DEBUG
@ -43,7 +43,7 @@ IRCClient::~IRCClient()
{
}
void IRCClient::set_server(const String &hostname, int port)
void IRCClient::set_server(const String& hostname, int port)
{
m_hostname = hostname;
m_port = port;
@ -112,7 +112,8 @@ void IRCClient::process_line(ByteBuffer&& line)
InStartOfParameter,
InParameter,
InTrailingParameter,
} state = Start;
} state
= Start;
for (int i = 0; i < line.size(); ++i) {
char ch = line[i];
@ -216,8 +217,7 @@ void IRCClient::handle(const Message& msg)
printf("IRCClient::execute: prefix='%s', command='%s', arguments=%d\n",
msg.prefix.characters(),
msg.command.characters(),
msg.arguments.size()
);
msg.arguments.size());
int i = 0;
for (auto& arg : msg.arguments) {
@ -231,16 +231,26 @@ void IRCClient::handle(const Message& msg)
if (is_numeric) {
switch (numeric) {
case RPL_WHOISCHANNELS: return handle_rpl_whoischannels(msg);
case RPL_ENDOFWHOIS: return handle_rpl_endofwhois(msg);
case RPL_WHOISOPERATOR: return handle_rpl_whoisoperator(msg);
case RPL_WHOISSERVER: return handle_rpl_whoisserver(msg);
case RPL_WHOISUSER: return handle_rpl_whoisuser(msg);
case RPL_WHOISIDLE: return handle_rpl_whoisidle(msg);
case RPL_TOPICWHOTIME: return handle_rpl_topicwhotime(msg);
case RPL_TOPIC: return handle_rpl_topic(msg);
case RPL_NAMREPLY: return handle_rpl_namreply(msg);
case RPL_ENDOFNAMES: return handle_rpl_endofnames(msg);
case RPL_WHOISCHANNELS:
return handle_rpl_whoischannels(msg);
case RPL_ENDOFWHOIS:
return handle_rpl_endofwhois(msg);
case RPL_WHOISOPERATOR:
return handle_rpl_whoisoperator(msg);
case RPL_WHOISSERVER:
return handle_rpl_whoisserver(msg);
case RPL_WHOISUSER:
return handle_rpl_whoisuser(msg);
case RPL_WHOISIDLE:
return handle_rpl_whoisidle(msg);
case RPL_TOPICWHOTIME:
return handle_rpl_topicwhotime(msg);
case RPL_TOPIC:
return handle_rpl_topic(msg);
case RPL_NAMREPLY:
return handle_rpl_namreply(msg);
case RPL_ENDOFNAMES:
return handle_rpl_endofnames(msg);
}
}
@ -441,7 +451,7 @@ void IRCClient::handle_rpl_topic(const Message& msg)
return;
auto& channel_name = msg.arguments[1];
auto& topic = msg.arguments[2];
ensure_channel(channel_name).handle_topic({ }, topic);
ensure_channel(channel_name).handle_topic({}, topic);
// FIXME: Handle RPL_TOPICWHOTIME so we can know who set it and when.
}
@ -502,8 +512,7 @@ void IRCClient::handle_rpl_whoisuser(const Message& msg)
nick.characters(),
username.characters(),
host.characters(),
realname.characters()
));
realname.characters()));
}
void IRCClient::handle_rpl_whoisidle(const Message& msg)
@ -541,8 +550,7 @@ void IRCClient::handle_rpl_topicwhotime(const Message& msg)
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec
);
tm->tm_sec);
}
ensure_channel(channel_name).add_message(String::format("*** (set by %s at %s)", nick.characters(), setat.characters()), Color::Blue);
}

View file

@ -1,8 +1,8 @@
#include "IRCLogBufferModel.h"
#include "IRCLogBuffer.h"
#include <SharedGraphics/Font.h>
#include <stdio.h>
#include <time.h>
#include <SharedGraphics/Font.h>
IRCLogBufferModel::IRCLogBufferModel(Retained<IRCLogBuffer>&& log_buffer)
: m_log_buffer(move(log_buffer))
@ -26,9 +26,12 @@ int IRCLogBufferModel::column_count(const GModelIndex&) const
String IRCLogBufferModel::column_name(int column) const
{
switch (column) {
case Column::Timestamp: return "Time";
case Column::Name: return "Name";
case Column::Text: return "Text";
case Column::Timestamp:
return "Time";
case Column::Name:
return "Name";
case Column::Text:
return "Text";
}
ASSERT_NOT_REACHED();
}
@ -36,9 +39,12 @@ String IRCLogBufferModel::column_name(int column) const
GModel::ColumnMetadata IRCLogBufferModel::column_metadata(int column) const
{
switch (column) {
case Column::Timestamp: return { 60, TextAlignment::CenterLeft };
case Column::Name: return { 70, TextAlignment::CenterRight, &Font::default_bold_font() };
case Column::Text: return { 800, TextAlignment::CenterLeft };
case Column::Timestamp:
return { 60, TextAlignment::CenterLeft };
case Column::Name:
return { 70, TextAlignment::CenterRight, &Font::default_bold_font() };
case Column::Text:
return { 800, TextAlignment::CenterLeft };
}
ASSERT_NOT_REACHED();
}
@ -56,7 +62,8 @@ GVariant IRCLogBufferModel::data(const GModelIndex& index, Role role) const
if (entry.sender.is_empty())
return String::empty();
return String::format("<%c%s>", entry.prefix ? entry.prefix : ' ', entry.sender.characters());
case Column::Text: return entry.text;
case Column::Text:
return entry.text;
}
}
if (role == Role::ForegroundColor) {
@ -65,7 +72,7 @@ GVariant IRCLogBufferModel::data(const GModelIndex& index, Role role) const
if (index.column() == Column::Text)
return m_log_buffer->at(index.row()).color;
}
return { };
return {};
}
void IRCLogBufferModel::update()

View file

@ -6,8 +6,7 @@ class IRCLogBuffer;
class IRCLogBufferModel final : public GModel {
public:
enum Column
{
enum Column {
Timestamp = 0,
Name,
Text,

View file

@ -1,13 +1,13 @@
#include "IRCWindow.h"
#include "IRCClient.h"
#include "IRCChannel.h"
#include "IRCChannelMemberListModel.h"
#include "IRCClient.h"
#include "IRCLogBufferModel.h"
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GTableView.h>
#include <LibGUI/GTextEditor.h>
#include <LibGUI/GTextBox.h>
#include <LibGUI/GSplitter.h>
#include <LibGUI/GTableView.h>
#include <LibGUI/GTextBox.h>
#include <LibGUI/GTextEditor.h>
IRCWindow::IRCWindow(IRCClient& client, void* owner, Type type, const String& name, GWidget* parent)
: GWidget(parent)

View file

@ -11,8 +11,7 @@ class GTextEditor;
class IRCWindow : public GWidget {
public:
enum Type
{
enum Type {
Server,
Channel,
Query,

View file

@ -1,7 +1,7 @@
#include "IRCWindowListModel.h"
#include "IRCWindow.h"
#include "IRCClient.h"
#include "IRCChannel.h"
#include "IRCClient.h"
#include "IRCWindow.h"
#include <stdio.h>
#include <time.h>
@ -27,7 +27,8 @@ int IRCWindowListModel::column_count(const GModelIndex&) const
String IRCWindowListModel::column_name(int column) const
{
switch (column) {
case Column::Name: return "Name";
case Column::Name:
return "Name";
}
ASSERT_NOT_REACHED();
}
@ -35,7 +36,8 @@ String IRCWindowListModel::column_name(int column) const
GModel::ColumnMetadata IRCWindowListModel::column_metadata(int column) const
{
switch (column) {
case Column::Name: return { 70, TextAlignment::CenterLeft };
case Column::Name:
return { 70, TextAlignment::CenterLeft };
}
ASSERT_NOT_REACHED();
}
@ -64,7 +66,7 @@ GVariant IRCWindowListModel::data(const GModelIndex& index, Role role) const
}
}
}
return { };
return {};
}
void IRCWindowListModel::update()

View file

@ -8,8 +8,7 @@ class IRCWindow;
class IRCWindowListModel final : public GModel {
public:
enum Column
{
enum Column {
Name,
};

View file

@ -1,6 +1,6 @@
#include "IRCAppWindow.h"
#include "IRCClient.h"
#include <LibGUI/GApplication.h>
#include "IRCAppWindow.h"
#include <stdio.h>
int main(int argc, char** argv)

View file

@ -1,16 +1,16 @@
#include <SharedGraphics/GraphicsBitmap.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GButton.h>
#include <LibCore/CConfigFile.h>
#include <LibCore/CUserInfo.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibCore/CConfigFile.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <SharedGraphics/GraphicsBitmap.h>
#include <errno.h>
#include <LibCore/CUserInfo.h>
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
static GWindow* make_launcher_window();
@ -47,7 +47,7 @@ public:
set_icon(GraphicsBitmap::load_from_file(icon_path));
set_preferred_size({ 50, 50 });
set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
on_click = [this] (GButton&) {
on_click = [this](GButton&) {
pid_t child_pid = fork();
if (!child_pid) {
int rc = execl(m_executable_path.characters(), m_executable_path.characters(), nullptr);
@ -55,7 +55,8 @@ public:
perror("execl");
}
};
} virtual ~LauncherButton() { }
}
virtual ~LauncherButton() {}
private:
String m_executable_path;
@ -78,9 +79,9 @@ GWindow* make_launcher_window()
for (auto& group : config->groups()) {
new LauncherButton(config->read_entry(group, "Name", group),
config->read_entry(group, "Icon", ""),
config->read_entry(group, "Path", ""),
widget);
config->read_entry(group, "Icon", ""),
config->read_entry(group, "Path", ""),
widget);
}
return window;

3
Applications/PaintBrush/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.o
*.d
PaintBrush

View file

@ -0,0 +1,55 @@
#include "BucketTool.h"
#include "PaintableWidget.h"
#include <AK/Queue.h>
#include <AK/SinglyLinkedList.h>
#include <LibGUI/GPainter.h>
#include <SharedGraphics/GraphicsBitmap.h>
#include <stdio.h>
BucketTool::BucketTool()
{
}
BucketTool::~BucketTool()
{
}
static void flood_fill(GraphicsBitmap& bitmap, const Point& start_position, Color target_color, Color fill_color)
{
ASSERT(bitmap.format() == GraphicsBitmap::Format::RGB32);
Queue<Point> queue;
queue.enqueue(Point(start_position));
while (!queue.is_empty()) {
auto position = queue.dequeue();
if (bitmap.get_pixel<GraphicsBitmap::Format::RGB32>(position.x(), position.y()) != target_color)
continue;
bitmap.set_pixel<GraphicsBitmap::Format::RGB32>(position.x(), position.y(), fill_color);
if (position.x() != 0)
queue.enqueue(position.translated(-1, 0));
if (position.x() != bitmap.width() - 1)
queue.enqueue(position.translated(1, 0));
if (position.y() != 0)
queue.enqueue(position.translated(0, -1));
if (position.y() != bitmap.height() - 1)
queue.enqueue(position.translated(0, 1));
}
}
void BucketTool::on_mousedown(PaintableWidget& paintable_widget, GMouseEvent& event)
{
if (!paintable_widget.rect().contains(event.position()))
return;
GPainter painter(paintable_widget.bitmap());
auto target_color = paintable_widget.bitmap().get_pixel(event.x(), event.y());
flood_fill(paintable_widget.bitmap(), event.position(), target_color, paintable_widget.color_for(event));
paintable_widget.update();
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "Tool.h"
class BucketTool final : public Tool {
public:
BucketTool();
virtual ~BucketTool() override;
virtual void on_mousedown(PaintableWidget&, GMouseEvent&) override;
private:
virtual const char* class_name() const override { return "BucketTool"; }
};

View file

@ -0,0 +1,28 @@
include ../../Makefile.common
OBJS = \
PaintableWidget.o \
PaletteWidget.o \
ToolboxWidget.o \
Tool.o \
PenTool.o \
BucketTool.o \
main.o
APP = PaintBrush
DEFINES += -DUSERLAND
all: $(APP)
$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -lcore -lc
.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
-include $(OBJS:%.o=%.d)
clean:
@echo "CLEAN"; rm -f $(APP) $(OBJS) *.d

View file

@ -0,0 +1,60 @@
#include "PaintableWidget.h"
#include "Tool.h"
#include <LibGUI/GPainter.h>
#include <SharedGraphics/GraphicsBitmap.h>
static PaintableWidget* s_the;
PaintableWidget& PaintableWidget::the()
{
return *s_the;
}
PaintableWidget::PaintableWidget(GWidget* parent)
: GWidget(parent)
{
ASSERT(!s_the);
s_the = this;
set_fill_with_background_color(true);
set_background_color(Color::MidGray);
m_bitmap = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, { 600, 400 });
m_bitmap->fill(Color::White);
}
PaintableWidget::~PaintableWidget()
{
}
void PaintableWidget::paint_event(GPaintEvent& event)
{
GPainter painter(*this);
painter.add_clip_rect(event.rect());
painter.blit({ 0, 0 }, *m_bitmap, m_bitmap->rect());
}
Color PaintableWidget::color_for(const GMouseEvent& event)
{
if (event.buttons() & GMouseButton::Left)
return m_primary_color;
if (event.buttons() & GMouseButton::Right)
return m_secondary_color;
ASSERT_NOT_REACHED();
}
void PaintableWidget::mousedown_event(GMouseEvent& event)
{
if (m_tool)
m_tool->on_mousedown(*this, event);
}
void PaintableWidget::mouseup_event(GMouseEvent& event)
{
if (m_tool)
m_tool->on_mouseup(*this, event);
}
void PaintableWidget::mousemove_event(GMouseEvent& event)
{
if (m_tool)
m_tool->on_mousemove(*this, event);
}

View file

@ -0,0 +1,42 @@
#pragma once
#include <LibGUI/GWidget.h>
class Tool;
class PaintableWidget final : public GWidget {
public:
static PaintableWidget& the();
explicit PaintableWidget(GWidget* parent);
virtual ~PaintableWidget() override;
virtual const char* class_name() const override { return "PaintableWidget"; }
Color primary_color() const { return m_primary_color; }
Color secondary_color() const { return m_secondary_color; }
void set_primary_color(Color color) { m_primary_color = color; }
void set_secondary_color(Color color) { m_secondary_color = color; }
void set_tool(Tool* tool) { m_tool = tool; }
Tool* tool() { return m_tool; }
Color color_for(const GMouseEvent&);
GraphicsBitmap& bitmap() { return *m_bitmap; }
const GraphicsBitmap& bitmap() const { return *m_bitmap; }
private:
virtual void paint_event(GPaintEvent&) override;
virtual void mousedown_event(GMouseEvent&) override;
virtual void mouseup_event(GMouseEvent&) override;
virtual void mousemove_event(GMouseEvent&) override;
RetainPtr<GraphicsBitmap> m_bitmap;
Color m_primary_color { Color::Black };
Color m_secondary_color { Color::White };
Tool* m_tool { nullptr };
};

View file

@ -0,0 +1,131 @@
#include "PaletteWidget.h"
#include "PaintableWidget.h"
#include <LibGUI/GBoxLayout.h>
class ColorWidget : public GFrame {
public:
explicit ColorWidget(Color color, PaletteWidget& palette_widget, GWidget* parent)
: GFrame(parent)
, m_palette_widget(palette_widget)
, m_color(color)
{
set_frame_thickness(2);
set_frame_shadow(FrameShadow::Sunken);
set_frame_shape(FrameShape::Container);
}
virtual ~ColorWidget() override
{
}
virtual void mousedown_event(GMouseEvent& event) override
{
if (event.button() == GMouseButton::Left)
m_palette_widget.set_primary_color(m_color);
else if (event.button() == GMouseButton::Right)
m_palette_widget.set_secondary_color(m_color);
}
private:
PaletteWidget& m_palette_widget;
Color m_color;
};
PaletteWidget::PaletteWidget(PaintableWidget& paintable_widget, GWidget* parent)
: GFrame(parent)
, m_paintable_widget(paintable_widget)
{
set_frame_shape(FrameShape::Panel);
set_frame_shadow(FrameShadow::Raised);
set_frame_thickness(0);
set_fill_with_background_color(true);
set_background_color(Color::LightGray);
set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
set_preferred_size({ 0, 34 });
m_secondary_color_widget = new GFrame(this);
m_secondary_color_widget->set_frame_thickness(2);
m_secondary_color_widget->set_frame_shape(FrameShape::Container);
m_secondary_color_widget->set_frame_shadow(FrameShadow::Sunken);
m_secondary_color_widget->set_relative_rect({ 2, 2, 60, 31 });
m_secondary_color_widget->set_fill_with_background_color(true);
set_secondary_color(paintable_widget.secondary_color());
m_primary_color_widget = new GFrame(this);
m_primary_color_widget->set_frame_thickness(2);
m_primary_color_widget->set_frame_shape(FrameShape::Container);
m_primary_color_widget->set_frame_shadow(FrameShadow::Sunken);
Rect rect { 0, 0, 38, 15 };
rect.center_within(m_secondary_color_widget->relative_rect());
m_primary_color_widget->set_relative_rect(rect);
m_primary_color_widget->set_fill_with_background_color(true);
set_primary_color(paintable_widget.primary_color());
auto* color_container = new GWidget(this);
color_container->set_relative_rect(m_secondary_color_widget->relative_rect().right() + 2, 2, 500, 32);
color_container->set_layout(make<GBoxLayout>(Orientation::Vertical));
color_container->layout()->set_spacing(1);
auto* top_color_container = new GWidget(color_container);
top_color_container->set_layout(make<GBoxLayout>(Orientation::Horizontal));
top_color_container->layout()->set_spacing(1);
auto* bottom_color_container = new GWidget(color_container);
bottom_color_container->set_layout(make<GBoxLayout>(Orientation::Horizontal));
bottom_color_container->layout()->set_spacing(1);
auto add_color_widget = [&] (GWidget* container, Color color) {
auto* color_widget = new ColorWidget(color, *this, container);
color_widget->set_fill_with_background_color(true);
color_widget->set_background_color(color);
};
add_color_widget(top_color_container, Color::from_rgb(0x000000));
add_color_widget(top_color_container, Color::from_rgb(0x808080));
add_color_widget(top_color_container, Color::from_rgb(0x800000));
add_color_widget(top_color_container, Color::from_rgb(0x808000));
add_color_widget(top_color_container, Color::from_rgb(0x008000));
add_color_widget(top_color_container, Color::from_rgb(0x008080));
add_color_widget(top_color_container, Color::from_rgb(0x000080));
add_color_widget(top_color_container, Color::from_rgb(0x800080));
add_color_widget(top_color_container, Color::from_rgb(0x808040));
add_color_widget(top_color_container, Color::from_rgb(0x004040));
add_color_widget(top_color_container, Color::from_rgb(0x0080ff));
add_color_widget(top_color_container, Color::from_rgb(0x004080));
add_color_widget(top_color_container, Color::from_rgb(0x8000ff));
add_color_widget(top_color_container, Color::from_rgb(0x804000));
add_color_widget(bottom_color_container, Color::from_rgb(0xffffff));
add_color_widget(bottom_color_container, Color::from_rgb(0xc0c0c0));
add_color_widget(bottom_color_container, Color::from_rgb(0xff0000));
add_color_widget(bottom_color_container, Color::from_rgb(0xffff00));
add_color_widget(bottom_color_container, Color::from_rgb(0x00ff00));
add_color_widget(bottom_color_container, Color::from_rgb(0x00ffff));
add_color_widget(bottom_color_container, Color::from_rgb(0x0000ff));
add_color_widget(bottom_color_container, Color::from_rgb(0xff00ff));
add_color_widget(bottom_color_container, Color::from_rgb(0xffff80));
add_color_widget(bottom_color_container, Color::from_rgb(0x00ff80));
add_color_widget(bottom_color_container, Color::from_rgb(0x80ffff));
add_color_widget(bottom_color_container, Color::from_rgb(0x8080ff));
add_color_widget(bottom_color_container, Color::from_rgb(0xff0080));
add_color_widget(bottom_color_container, Color::from_rgb(0xff8040));
}
PaletteWidget::~PaletteWidget()
{
}
void PaletteWidget::set_primary_color(Color color)
{
m_paintable_widget.set_primary_color(color);
m_primary_color_widget->set_background_color(color);
m_primary_color_widget->update();
}
void PaletteWidget::set_secondary_color(Color color)
{
m_paintable_widget.set_secondary_color(color);
m_secondary_color_widget->set_background_color(color);
m_secondary_color_widget->update();
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <LibGUI/GFrame.h>
class PaintableWidget;
class PaletteWidget final : public GFrame {
public:
explicit PaletteWidget(PaintableWidget&, GWidget* parent);
virtual ~PaletteWidget() override;
virtual const char* class_name() const override { return "PaletteWidget"; }
void set_primary_color(Color);
void set_secondary_color(Color);
private:
PaintableWidget& m_paintable_widget;
GFrame* m_primary_color_widget { nullptr };
GFrame* m_secondary_color_widget { nullptr };
};

View file

@ -0,0 +1,48 @@
#include "PenTool.h"
#include "PaintableWidget.h"
#include <LibGUI/GPainter.h>
PenTool::PenTool()
{
}
PenTool::~PenTool()
{
}
void PenTool::on_mousedown(PaintableWidget& paintable_widget, GMouseEvent& event)
{
if (event.button() != GMouseButton::Left && event.button() != GMouseButton::Right)
return;
GPainter painter(paintable_widget.bitmap());
painter.set_pixel(event.position(), paintable_widget.color_for(event));
paintable_widget.update({ event.position(), { 1, 1 } });
m_last_drawing_event_position = event.position();
}
void PenTool::on_mouseup(PaintableWidget&, GMouseEvent& event)
{
if (event.button() == GMouseButton::Left || event.button() == GMouseButton::Right)
m_last_drawing_event_position = { -1, -1 };
}
void PenTool::on_mousemove(PaintableWidget& paintable_widget, GMouseEvent& event)
{
if (!paintable_widget.rect().contains(event.position()))
return;
if (event.buttons() & GMouseButton::Left || event.buttons() & GMouseButton::Right) {
GPainter painter(paintable_widget.bitmap());
if (m_last_drawing_event_position != Point(-1, -1)) {
painter.draw_line(m_last_drawing_event_position, event.position(), paintable_widget.color_for(event));
paintable_widget.update();
} else {
painter.set_pixel(event.position(), paintable_widget.color_for(event));
paintable_widget.update({ event.position(), { 1, 1 } });
}
m_last_drawing_event_position = event.position();
}
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "Tool.h"
#include <SharedGraphics/Point.h>
class PenTool final : public Tool {
public:
PenTool();
virtual ~PenTool() override;
virtual void on_mousedown(PaintableWidget&, GMouseEvent&) override;
virtual void on_mousemove(PaintableWidget&, GMouseEvent&) override;
virtual void on_mouseup(PaintableWidget&, GMouseEvent&) override;
private:
virtual const char* class_name() const override { return "PenTool"; }
Point m_last_drawing_event_position { -1, -1 };
};

View file

@ -0,0 +1,9 @@
#include "Tool.h"
Tool::Tool()
{
}
Tool::~Tool()
{
}

View file

@ -0,0 +1,18 @@
#pragma once
class GMouseEvent;
class PaintableWidget;
class Tool {
public:
virtual ~Tool();
virtual const char* class_name() const = 0;
virtual void on_mousedown(PaintableWidget&, GMouseEvent&) { }
virtual void on_mousemove(PaintableWidget&, GMouseEvent&) { }
virtual void on_mouseup(PaintableWidget&, GMouseEvent&) { }
protected:
Tool();
};

View file

@ -0,0 +1,64 @@
#include "ToolboxWidget.h"
#include "BucketTool.h"
#include "PaintableWidget.h"
#include "PenTool.h"
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GButton.h>
#include <SharedGraphics/PNGLoader.h>
class ToolButton final : public GButton {
public:
ToolButton(const String& name, GWidget* parent, OwnPtr<Tool>&& tool)
: GButton(parent)
, m_tool(move(tool))
{
set_tooltip(name);
}
const Tool& tool() const { return *m_tool; }
Tool& tool() { return *m_tool; }
private:
OwnPtr<Tool> m_tool;
};
ToolboxWidget::ToolboxWidget(GWidget* parent)
: GFrame(parent)
{
set_background_color(Color::LightGray);
set_fill_with_background_color(true);
set_frame_thickness(1);
set_frame_shape(FrameShape::Panel);
set_frame_shadow(FrameShadow::Raised);
set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
set_preferred_size({ 48, 0 });
set_layout(make<GBoxLayout>(Orientation::Vertical));
layout()->set_margins({ 4, 4, 4, 4 });
auto add_tool = [&](const StringView& name, const StringView& icon_name, OwnPtr<Tool>&& tool) {
auto* button = new ToolButton(name, this, move(tool));
button->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
button->set_preferred_size({ 0, 32 });
button->set_checkable(true);
button->set_exclusive(true);
button->set_icon(load_png(String::format("/res/icons/paintbrush/%s.png", icon_name.characters())));
button->on_checked = [button](auto checked) {
if (checked)
PaintableWidget::the().set_tool(&button->tool());
else
PaintableWidget::the().set_tool(nullptr);
};
};
add_tool("Pen", "pen", make<PenTool>());
add_tool("Bucket Fill", "bucket", make<BucketTool>());
}
ToolboxWidget::~ToolboxWidget()
{
}

View file

@ -0,0 +1,13 @@
#pragma once
#include <LibGUI/GFrame.h>
class ToolboxWidget final : public GFrame {
public:
explicit ToolboxWidget(GWidget* parent);
virtual ~ToolboxWidget() override;
virtual const char* class_name() const override { return "ToolboxWidget"; }
private:
};

View file

@ -0,0 +1,58 @@
#include "PaintableWidget.h"
#include "PaletteWidget.h"
#include "ToolboxWidget.h"
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GWindow.h>
int main(int argc, char** argv)
{
GApplication app(argc, argv);
auto* window = new GWindow;
window->set_title("PaintBrush");
window->set_rect(100, 100, 640, 480);
auto* horizontal_container = new GWidget(nullptr);
window->set_main_widget(horizontal_container);
horizontal_container->set_layout(make<GBoxLayout>(Orientation::Horizontal));
horizontal_container->layout()->set_spacing(0);
auto* toolbox_widget = new ToolboxWidget(horizontal_container);
auto* vertical_container = new GWidget(horizontal_container);
vertical_container->set_layout(make<GBoxLayout>(Orientation::Vertical));
vertical_container->layout()->set_spacing(0);
auto* paintable_widget = new PaintableWidget(vertical_container);
auto* palette_widget = new PaletteWidget(*paintable_widget, vertical_container);
window->show();
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("PaintBrush");
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [](const GAction&) {
GApplication::the().quit(0);
return;
}));
menubar->add_menu(move(app_menu));
auto file_menu = make<GMenu>("File");
menubar->add_menu(move(file_menu));
auto edit_menu = make<GMenu>("Edit");
menubar->add_menu(move(edit_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(GAction::create("About", [](const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));
app.set_menubar(move(menubar));
return app.exec();
}

View file

@ -1,8 +1,8 @@
#include "MemoryStatsWidget.h"
#include "GraphWidget.h"
#include <LibGUI/GPainter.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GPainter.h>
#include <SharedGraphics/StylePainter.h>
#include <stdio.h>
#include <stdlib.h>
@ -21,7 +21,7 @@ MemoryStatsWidget::MemoryStatsWidget(GraphWidget& graph, GWidget* parent)
layout()->set_margins({ 0, 8, 0, 0 });
layout()->set_spacing(3);
auto build_widgets_for_label = [this] (const String& description) -> GLabel* {
auto build_widgets_for_label = [this](const String& description) -> GLabel* {
auto* container = new GWidget(this);
container->set_layout(make<GBoxLayout>(Orientation::Horizontal));
container->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);

View file

@ -2,8 +2,8 @@
#include "GraphWidget.h"
#include <LibCore/CFile.h>
#include <fcntl.h>
#include <stdio.h>
#include <pwd.h>
#include <stdio.h>
ProcessModel::ProcessModel(GraphWidget& graph)
: m_graph(graph)
@ -42,34 +42,56 @@ int ProcessModel::column_count(const GModelIndex&) const
String ProcessModel::column_name(int column) const
{
switch (column) {
case Column::Icon: return "";
case Column::PID: return "PID";
case Column::State: return "State";
case Column::User: return "User";
case Column::Priority: return "Pr";
case Column::Linear: return "Linear";
case Column::Physical: return "Physical";
case Column::CPU: return "CPU";
case Column::Name: return "Name";
case Column::Syscalls: return "Syscalls";
default: ASSERT_NOT_REACHED();
case Column::Icon:
return "";
case Column::PID:
return "PID";
case Column::State:
return "State";
case Column::User:
return "User";
case Column::Priority:
return "Pr";
case Column::Virtual:
return "Virtual";
case Column::Physical:
return "Physical";
case Column::CPU:
return "CPU";
case Column::Name:
return "Name";
case Column::Syscalls:
return "Syscalls";
default:
ASSERT_NOT_REACHED();
}
}
GModel::ColumnMetadata ProcessModel::column_metadata(int column) const
{
switch (column) {
case Column::Icon: return { 16, TextAlignment::CenterLeft };
case Column::PID: return { 32, TextAlignment::CenterRight };
case Column::State: return { 75, TextAlignment::CenterLeft };
case Column::Priority: return { 16, TextAlignment::CenterLeft };
case Column::User: return { 50, TextAlignment::CenterLeft };
case Column::Linear: return { 65, TextAlignment::CenterRight };
case Column::Physical: return { 65, TextAlignment::CenterRight };
case Column::CPU: return { 32, TextAlignment::CenterRight };
case Column::Name: return { 140, TextAlignment::CenterLeft };
case Column::Syscalls: return { 60, TextAlignment::CenterRight };
default: ASSERT_NOT_REACHED();
case Column::Icon:
return { 16, TextAlignment::CenterLeft };
case Column::PID:
return { 32, TextAlignment::CenterRight };
case Column::State:
return { 75, TextAlignment::CenterLeft };
case Column::Priority:
return { 16, TextAlignment::CenterLeft };
case Column::User:
return { 50, TextAlignment::CenterLeft };
case Column::Virtual:
return { 65, TextAlignment::CenterRight };
case Column::Physical:
return { 65, TextAlignment::CenterRight };
case Column::CPU:
return { 32, TextAlignment::CenterRight };
case Column::Name:
return { 140, TextAlignment::CenterLeft };
case Column::Syscalls:
return { 60, TextAlignment::CenterRight };
default:
ASSERT_NOT_REACHED();
}
}
@ -87,10 +109,14 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
if (role == Role::Sort) {
switch (index.column()) {
case Column::Icon: return 0;
case Column::PID: return process.current_state.pid;
case Column::State: return process.current_state.state;
case Column::User: return process.current_state.user;
case Column::Icon:
return 0;
case Column::PID:
return process.current_state.pid;
case Column::State:
return process.current_state.state;
case Column::User:
return process.current_state.user;
case Column::Priority:
if (process.current_state.priority == "Idle")
return 0;
@ -102,23 +128,32 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
return 3;
ASSERT_NOT_REACHED();
return 3;
case Column::Linear: return (int)process.current_state.linear;
case Column::Physical: return (int)process.current_state.physical;
case Column::CPU: return process.current_state.cpu_percent;
case Column::Name: return process.current_state.name;
case Column::Virtual:
return (int)process.current_state.virtual_size;
case Column::Physical:
return (int)process.current_state.physical_size;
case Column::CPU:
return process.current_state.cpu_percent;
case Column::Name:
return process.current_state.name;
// FIXME: GVariant with unsigned?
case Column::Syscalls: return (int)process.current_state.syscalls;
case Column::Syscalls:
return (int)process.current_state.syscalls;
}
ASSERT_NOT_REACHED();
return { };
return {};
}
if (role == Role::Display) {
switch (index.column()) {
case Column::Icon: return *m_generic_process_icon;
case Column::PID: return process.current_state.pid;
case Column::State: return process.current_state.state;
case Column::User: return process.current_state.user;
case Column::Icon:
return *m_generic_process_icon;
case Column::PID:
return process.current_state.pid;
case Column::State:
return process.current_state.state;
case Column::User:
return process.current_state.user;
case Column::Priority:
if (process.current_state.priority == "Idle")
return String::empty();
@ -129,16 +164,21 @@ GVariant ProcessModel::data(const GModelIndex& index, Role role) const
if (process.current_state.priority == "Normal")
return *m_normal_priority_icon;
return process.current_state.priority;
case Column::Linear: return pretty_byte_size(process.current_state.linear);
case Column::Physical: return pretty_byte_size(process.current_state.physical);
case Column::CPU: return process.current_state.cpu_percent;
case Column::Name: return process.current_state.name;
case Column::Virtual:
return pretty_byte_size(process.current_state.virtual_size);
case Column::Physical:
return pretty_byte_size(process.current_state.physical_size);
case Column::CPU:
return process.current_state.cpu_percent;
case Column::Name:
return process.current_state.name;
// FIXME: It's weird that GVariant doesn't support unsigned ints. Should it?
case Column::Syscalls: return (int)process.current_state.syscalls;
case Column::Syscalls:
return (int)process.current_state.syscalls;
}
}
return { };
return {};
}
void ProcessModel::update()
@ -181,9 +221,9 @@ void ProcessModel::update()
ASSERT(ok);
state.state = parts[7];
state.name = parts[11];
state.linear = parts[12].to_uint(ok);
state.virtual_size = parts[12].to_uint(ok);
ASSERT(ok);
state.physical = parts[13].to_uint(ok);
state.physical_size = parts[13].to_uint(ok);
ASSERT(ok);
sum_nsched += nsched;
{

View file

@ -11,8 +11,7 @@ class GraphWidget;
class ProcessModel final : public GModel {
public:
enum Column
{
enum Column {
Icon = 0,
Name,
CPU,
@ -20,7 +19,7 @@ public:
Priority,
User,
PID,
Linear,
Virtual,
Physical,
Syscalls,
__Count
@ -48,8 +47,8 @@ private:
String state;
String user;
String priority;
size_t linear;
size_t physical;
size_t virtual_size;
size_t physical_size;
unsigned syscalls;
float cpu_percent;
};

View file

@ -1,20 +1,20 @@
#include <LibCore/CTimer.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GGroupBox.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GTabWidget.h>
#include <LibGUI/GLabel.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include "ProcessTableView.h"
#include "MemoryStatsWidget.h"
#include "GraphWidget.h"
#include "MemoryStatsWidget.h"
#include "ProcessTableView.h"
#include <LibCore/CTimer.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GGroupBox.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GTabWidget.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
@ -46,7 +46,7 @@ int main(int argc, char** argv)
cpu_graph->set_max(100);
cpu_graph->set_text_color(Color::Green);
cpu_graph->set_graph_color(Color::from_rgb(0x00bb00));
cpu_graph->text_formatter = [] (int value, int) {
cpu_graph->text_formatter = [](int value, int) {
return String::format("%d%%", value);
};
@ -58,7 +58,7 @@ int main(int argc, char** argv)
auto* memory_graph = new GraphWidget(memory_graph_group_box);
memory_graph->set_text_color(Color::Cyan);
memory_graph->set_graph_color(Color::from_rgb(0x00bbbb));
memory_graph->text_formatter = [] (int value, int max) {
memory_graph->text_formatter = [](int value, int max) {
return String::format("%d / %d KB", value, max);
};
@ -78,19 +78,19 @@ int main(int argc, char** argv)
memory_stats_widget->refresh();
});
auto kill_action = GAction::create("Kill process", GraphicsBitmap::load_from_file("/res/icons/kill16.png"), [process_table_view] (const GAction&) {
auto kill_action = GAction::create("Kill process", GraphicsBitmap::load_from_file("/res/icons/kill16.png"), [process_table_view](const GAction&) {
pid_t pid = process_table_view->selected_pid();
if (pid != -1)
kill(pid, SIGKILL);
});
auto stop_action = GAction::create("Stop process", GraphicsBitmap::load_from_file("/res/icons/stop16.png"), [process_table_view] (const GAction&) {
auto stop_action = GAction::create("Stop process", GraphicsBitmap::load_from_file("/res/icons/stop16.png"), [process_table_view](const GAction&) {
pid_t pid = process_table_view->selected_pid();
if (pid != -1)
kill(pid, SIGSTOP);
});
auto continue_action = GAction::create("Continue process", GraphicsBitmap::load_from_file("/res/icons/continue16.png"), [process_table_view] (const GAction&) {
auto continue_action = GAction::create("Continue process", GraphicsBitmap::load_from_file("/res/icons/continue16.png"), [process_table_view](const GAction&) {
pid_t pid = process_table_view->selected_pid();
if (pid != -1)
kill(pid, SIGCONT);
@ -102,7 +102,7 @@ int main(int argc, char** argv)
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("Process Manager");
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [] (const GAction&) {
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [](const GAction&) {
GApplication::the().quit(0);
return;
}));
@ -115,25 +115,25 @@ int main(int argc, char** argv)
menubar->add_menu(move(process_menu));
auto frequency_menu = make<GMenu>("Frequency");
frequency_menu->add_action(GAction::create("0.25 sec", [refresh_timer] (auto&) {
frequency_menu->add_action(GAction::create("0.25 sec", [refresh_timer](auto&) {
refresh_timer->restart(250);
}));
frequency_menu->add_action(GAction::create("0.5 sec", [refresh_timer] (auto&) {
frequency_menu->add_action(GAction::create("0.5 sec", [refresh_timer](auto&) {
refresh_timer->restart(500);
}));
frequency_menu->add_action(GAction::create("1 sec", [refresh_timer] (auto&) {
frequency_menu->add_action(GAction::create("1 sec", [refresh_timer](auto&) {
refresh_timer->restart(1000);
}));
frequency_menu->add_action(GAction::create("3 sec", [refresh_timer] (auto&) {
frequency_menu->add_action(GAction::create("3 sec", [refresh_timer](auto&) {
refresh_timer->restart(3000);
}));
frequency_menu->add_action(GAction::create("5 sec", [refresh_timer] (auto&) {
frequency_menu->add_action(GAction::create("5 sec", [refresh_timer](auto&) {
refresh_timer->restart(5000);
}));
menubar->add_menu(move(frequency_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(GAction::create("About", [] (const GAction&) {
help_menu->add_action(GAction::create("About", [](const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));

View file

@ -1,8 +1,8 @@
#include "TaskbarButton.h"
#include <WindowServer/WSAPITypes.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GEventLoop.h>
#include <LibGUI/GMenu.h>
#include <WindowServer/WSAPITypes.h>
static void set_window_minimized_state(const WindowIdentifier& identifier, bool minimized)
{
@ -34,13 +34,13 @@ GMenu& TaskbarButton::ensure_menu()
{
if (!m_menu) {
m_menu = make<GMenu>("");
m_menu->add_action(GAction::create("Minimize", [this] (auto&) {
m_menu->add_action(GAction::create("Minimize", [this](auto&) {
set_window_minimized_state(m_identifier, true);
}));
m_menu->add_action(GAction::create("Unminimize", [this] (auto&) {
m_menu->add_action(GAction::create("Unminimize", [this](auto&) {
set_window_minimized_state(m_identifier, false);
}));
m_menu->add_action(GAction::create("Close", [this] (auto&) {
m_menu->add_action(GAction::create("Close", [this](auto&) {
dbgprintf("FIXME: Close!\n");
}));
}

View file

@ -1,11 +1,11 @@
#include "TaskbarWindow.h"
#include "TaskbarButton.h"
#include <LibGUI/GWindow.h>
#include <LibGUI/GDesktop.h>
#include <LibGUI/GEventLoop.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GDesktop.h>
#include <LibGUI/GEventLoop.h>
#include <LibGUI/GFrame.h>
#include <LibGUI/GWindow.h>
#include <WindowServer/WSAPITypes.h>
#include <stdio.h>
@ -19,7 +19,7 @@ TaskbarWindow::TaskbarWindow()
on_screen_rect_change(GDesktop::the().rect());
GDesktop::the().on_rect_change = [this] (const Rect& rect) { on_screen_rect_change(rect); };
GDesktop::the().on_rect_change = [this](const Rect& rect) { on_screen_rect_change(rect); };
auto* widget = new GFrame;
widget->set_fill_with_background_color(true);
@ -31,7 +31,7 @@ TaskbarWindow::TaskbarWindow()
widget->set_frame_shadow(FrameShadow::Raised);
set_main_widget(widget);
WindowList::the().aid_create_button = [this] (auto& identifier) {
WindowList::the().aid_create_button = [this](auto& identifier) {
return create_button(identifier);
};
}
@ -70,8 +70,7 @@ void TaskbarWindow::wm_event(GWMEvent& event)
auto& removed_event = static_cast<GWMWindowRemovedEvent&>(event);
dbgprintf("WM_WindowRemoved: client_id=%d, window_id=%d\n",
removed_event.client_id(),
removed_event.window_id()
);
removed_event.window_id());
#endif
WindowList::the().remove_window(identifier);
update();
@ -83,8 +82,7 @@ void TaskbarWindow::wm_event(GWMEvent& event)
dbgprintf("WM_WindowRectChanged: client_id=%d, window_id=%d, rect=%s\n",
changed_event.client_id(),
changed_event.window_id(),
changed_event.rect().to_string().characters()
);
changed_event.rect().to_string().characters());
#endif
break;
}
@ -94,8 +92,7 @@ void TaskbarWindow::wm_event(GWMEvent& event)
dbgprintf("WM_WindowIconChanged: client_id=%d, window_id=%d, icon_path=%s\n",
changed_event.client_id(),
changed_event.window_id(),
changed_event.icon_path().characters()
);
changed_event.icon_path().characters());
#endif
if (auto* window = WindowList::the().window(identifier)) {
window->set_icon_path(changed_event.icon_path());
@ -113,8 +110,7 @@ void TaskbarWindow::wm_event(GWMEvent& event)
changed_event.title().characters(),
changed_event.rect().to_string().characters(),
changed_event.is_active(),
changed_event.is_minimized()
);
changed_event.is_minimized());
#endif
if (!should_include_window(changed_event.window_type()))
break;

View file

@ -1,6 +1,6 @@
#include "WindowList.h"
#include <WindowServer/WSAPITypes.h>
#include <LibGUI/GEventLoop.h>
#include <WindowServer/WSAPITypes.h>
WindowList& WindowList::the()
{
@ -25,7 +25,7 @@ Window& WindowList::ensure_window(const WindowIdentifier& identifier)
return *it->value;
auto window = make<Window>(identifier);
window->set_button(aid_create_button(identifier));
window->button()->on_click = [window = window.ptr(), identifier] (GButton&) {
window->button()->on_click = [window = window.ptr(), identifier](GButton&) {
WSAPI_ClientMessage message;
if (window->is_minimized() || !window->is_active()) {
message.type = WSAPI_ClientMessage::Type::WM_SetActiveWindow;

View file

@ -1,5 +1,5 @@
#include <LibGUI/GApplication.h>
#include "TaskbarWindow.h"
#include <LibGUI/GApplication.h>
int main(int argc, char** argv)
{

View file

@ -1,3 +1,4 @@
*.o
*.d
Terminal
compile_commands.json

View file

@ -1,19 +1,19 @@
#include "Terminal.h"
#include "XtermColors.h"
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <AK/AKString.h>
#include <AK/StringBuilder.h>
#include <SharedGraphics/Font.h>
#include <LibGUI/GPainter.h>
#include <AK/StdLibExtras.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GWindow.h>
#include <AK/StringBuilder.h>
#include <Kernel/KeyCode.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GPainter.h>
#include <LibGUI/GWindow.h>
#include <SharedGraphics/Font.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
//#define TERMINAL_DEBUG
byte Terminal::Attribute::default_foreground_color = 7;
@ -30,8 +30,8 @@ Terminal::Terminal(int ptm_fd, RetainPtr<CConfigFile> config)
dbgprintf("Terminal: Load config file from %s\n", m_config->file_name().characters());
m_cursor_blink_timer.set_interval(m_config->read_num_entry("Text",
"CursorBlinkInterval",
500));
"CursorBlinkInterval",
500));
m_cursor_blink_timer.on_timeout = [this] {
m_cursor_blink_state = !m_cursor_blink_state;
update_cursor();
@ -43,7 +43,7 @@ Terminal::Terminal(int ptm_fd, RetainPtr<CConfigFile> config)
else
set_font(Font::load_from_file(font_entry));
m_notifier.on_ready_to_read = [this]{
m_notifier.on_ready_to_read = [this] {
byte buffer[BUFSIZ];
ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer));
if (nread < 0) {
@ -65,37 +65,48 @@ Terminal::Terminal(int ptm_fd, RetainPtr<CConfigFile> config)
m_line_height = font().glyph_height() + m_line_spacing;
set_size(m_config->read_num_entry("Window", "Width", 80),
m_config->read_num_entry("Window", "Height", 25));
m_config->read_num_entry("Window", "Height", 25));
}
Terminal::Line::Line(word columns)
: length(columns)
Terminal::Line::Line(word length)
{
characters = new byte[length];
attributes = new Attribute[length];
memset(characters, ' ', length);
set_length(length);
}
Terminal::Line::~Line()
{
delete [] characters;
delete [] attributes;
delete[] characters;
delete[] attributes;
}
void Terminal::Line::set_length(word new_length)
{
if (m_length == new_length)
return;
auto* new_characters = new byte[new_length];
auto* new_attributes = new Attribute[new_length];
memset(new_characters, ' ', new_length);
delete[] characters;
delete[] attributes;
characters = new_characters;
attributes = new_attributes;
m_length = new_length;
}
void Terminal::Line::clear(Attribute attribute)
{
if (dirty) {
memset(characters, ' ', length);
for (word i = 0 ; i < length; ++i)
memset(characters, ' ', m_length);
for (word i = 0; i < m_length; ++i)
attributes[i] = attribute;
return;
}
for (unsigned i = 0 ; i < length; ++i) {
for (unsigned i = 0; i < m_length; ++i) {
if (characters[i] != ' ')
dirty = true;
characters[i] = ' ';
}
for (unsigned i = 0 ; i < length; ++i) {
for (unsigned i = 0; i < m_length; ++i) {
if (attributes[i] != attribute)
dirty = true;
attributes[i] = attribute;
@ -104,10 +115,6 @@ void Terminal::Line::clear(Attribute attribute)
Terminal::~Terminal()
{
for (int i = 0; i < m_rows; ++i)
delete m_lines[i];
delete [] m_lines;
free(m_horizontal_tabs);
}
void Terminal::clear()
@ -137,6 +144,35 @@ static inline Color lookup_color(unsigned color)
return Color::from_rgb(xterm_colors[color]);
}
void Terminal::escape$h_l(bool should_set, bool question_param, const ParamVector& params)
{
int mode = 2;
if (params.size() > 0) {
mode = params[0];
}
if (!question_param) {
switch (mode) {
// FIXME: implement *something* for this
default:
unimplemented_escape();
break;
}
} else {
switch (mode) {
case 25:
// Hide cursor command, but doesn't need to be run (for now, because
// we don't do inverse control codes anyways)
if (should_set)
dbgprintf("Terminal: Hide Cursor escapecode recieved. Not needed: ignored.\n");
else
dbgprintf("Terminal: Show Cursor escapecode recieved. Not needed: ignored.\n");
break;
default:
break;
}
}
}
void Terminal::escape$m(const ParamVector& params)
{
if (params.is_empty()) {
@ -243,7 +279,7 @@ void Terminal::escape$t(const ParamVector& params)
{
if (params.size() < 1)
return;
dbgprintf("FIXME: escape$t: Ps: %u\n", params[0]);
dbgprintf("FIXME: escape$t: Ps: %u (param count: %d)\n", params[0], params.size());
}
void Terminal::escape$r(const ParamVector& params)
@ -254,7 +290,13 @@ void Terminal::escape$r(const ParamVector& params)
top = params[0];
if (params.size() >= 2)
bottom = params[1];
dbgprintf("FIXME: escape$r: Set scrolling region: %u-%u\n", top, bottom);
if ((bottom - top) < 2 || bottom > m_rows || top < 0) {
dbgprintf("Error: escape$r: scrolling region invalid: %u-%u\n", top, bottom);
return;
}
m_scroll_region_top = top - 1;
m_scroll_region_bottom = bottom - 1;
set_cursor(0, 0);
}
void Terminal::escape$H(const ParamVector& params)
@ -330,6 +372,15 @@ void Terminal::escape$G(const ParamVector& params)
set_cursor(m_cursor_row, new_column);
}
void Terminal::escape$b(const ParamVector& params)
{
if (params.size() < 1)
return;
for (unsigned i = 0; i < params[0]; ++i)
put_character_at(m_cursor_row, m_cursor_column++, m_last_char);
}
void Terminal::escape$d(const ParamVector& params)
{
int new_row = 1;
@ -373,7 +424,10 @@ void Terminal::escape$K(const ParamVector& params)
}
break;
case 2:
unimplemented_escape();
// Clear the complete line
for (int i = 0; i < m_columns; ++i) {
put_character_at(m_cursor_row, i, ' ');
}
break;
default:
unimplemented_escape();
@ -389,9 +443,8 @@ void Terminal::escape$J(const ParamVector& params)
switch (mode) {
case 0:
// Clear from cursor to end of screen.
for (int i = m_cursor_column; i < m_columns; ++i) {
for (int i = m_cursor_column; i < m_columns; ++i)
put_character_at(m_cursor_row, i, ' ');
}
for (int row = m_cursor_row + 1; row < m_rows; ++row) {
for (int column = 0; column < m_columns; ++column) {
put_character_at(row, column, ' ');
@ -399,8 +452,14 @@ void Terminal::escape$J(const ParamVector& params)
}
break;
case 1:
// FIXME: Clear from cursor to beginning of screen.
unimplemented_escape();
/// Clear from cursor to beginning of screen
for (int i = m_cursor_column - 1; i >= 0; --i)
put_character_at(m_cursor_row, i, ' ');
for (int row = m_cursor_row - 1; row >= 0; --row) {
for (int column = 0; column < m_columns; ++column) {
put_character_at(row, column, ' ');
}
}
break;
case 2:
clear();
@ -415,6 +474,42 @@ void Terminal::escape$J(const ParamVector& params)
}
}
void Terminal::escape$S(const ParamVector& params)
{
int count = 1;
if (params.size() >= 1)
count = params[0];
for (word i = 0; i < count; i++)
scroll_up();
}
void Terminal::escape$T(const ParamVector& params)
{
int count = 1;
if (params.size() >= 1)
count = params[0];
for (word i = 0; i < count; i++)
scroll_down();
}
void Terminal::escape$L(const ParamVector& params)
{
int count = 1;
if (params.size() >= 1)
count = params[0];
invalidate_cursor();
for (; count > 0; --count) {
m_lines.insert(m_cursor_row + m_scroll_region_top, make<Line>(m_columns));
if (m_scroll_region_bottom + 1 < m_lines.size())
m_lines.remove(m_scroll_region_bottom + 1);
else
m_lines.remove(m_lines.size() - 1);
}
m_need_full_flush = true;
}
void Terminal::escape$M(const ParamVector& params)
{
int count = 1;
@ -426,12 +521,38 @@ void Terminal::escape$M(const ParamVector& params)
return;
}
int max_count = m_rows - m_cursor_row;
int max_count = m_rows - (m_scroll_region_top + m_cursor_row);
count = min(count, max_count);
dbgprintf("Delete %d line(s) starting from %d\n", count, m_cursor_row);
// FIXME: Implement.
ASSERT_NOT_REACHED();
for (int c = count; c > 0; --c) {
m_lines.remove(m_cursor_row + m_scroll_region_top);
if (m_scroll_region_bottom < m_lines.size())
m_lines.insert(m_scroll_region_bottom, make<Line>(m_columns));
else
m_lines.append(make<Line>(m_columns));
}
}
void Terminal::escape$P(const ParamVector& params)
{
int num = 1;
if (params.size() >= 1)
num = params[0];
if (num == 0)
num = 1;
auto& line = this->line(m_cursor_row);
// Move n characters of line to the left
for (int i = m_cursor_column; i < line.m_length - num; i++)
line.characters[i] = line.characters[i + num];
// Fill remainder of line with blanks
for (int i = line.m_length - num; i < line.m_length; i++)
line.characters[i] = ' ';
line.dirty = true;
}
void Terminal::execute_xterm_command()
@ -457,42 +578,122 @@ void Terminal::execute_xterm_command()
void Terminal::execute_escape_sequence(byte final)
{
bool question_param = false;
m_final = final;
auto paramparts = String::copy(m_parameters).split(';');
ParamVector params;
if (m_parameters.size() > 0 && m_parameters[0] == '?') {
question_param = true;
m_parameters.remove(0);
}
auto paramparts = String::copy(m_parameters).split(';');
for (auto& parampart : paramparts) {
bool ok;
unsigned value = parampart.to_uint(ok);
if (!ok) {
// FIXME: Should we do something else?
m_parameters.clear_with_capacity();
m_intermediates.clear_with_capacity();
// FIXME: Should we do something else?
return;
}
params.append(value);
}
#if defined(TERMINAL_DEBUG)
dbgprintf("Terminal::execute_escape_sequence: Handled final '%c'\n", final);
dbgprintf("Params: ");
for (auto& p : params) {
dbgprintf("%d ", p);
}
dbgprintf("\b\n");
#endif
switch (final) {
case 'A': escape$A(params); break;
case 'B': escape$B(params); break;
case 'C': escape$C(params); break;
case 'D': escape$D(params); break;
case 'H': escape$H(params); break;
case 'J': escape$J(params); break;
case 'K': escape$K(params); break;
case 'M': escape$M(params); break;
case 'G': escape$G(params); break;
case 'X': escape$X(params); break;
case 'd': escape$d(params); break;
case 'm': escape$m(params); break;
case 's': escape$s(params); break;
case 'u': escape$u(params); break;
case 't': escape$t(params); break;
case 'r': escape$r(params); break;
case 'A':
escape$A(params);
break;
case 'B':
escape$B(params);
break;
case 'C':
escape$C(params);
break;
case 'D':
escape$D(params);
break;
case 'H':
escape$H(params);
break;
case 'J':
escape$J(params);
break;
case 'K':
escape$K(params);
break;
case 'M':
escape$M(params);
break;
case 'P':
escape$P(params);
break;
case 'S':
escape$S(params);
break;
case 'T':
escape$T(params);
break;
case 'L':
escape$L(params);
break;
case 'G':
escape$G(params);
break;
case 'X':
escape$X(params);
break;
case 'b':
escape$b(params);
break;
case 'd':
escape$d(params);
break;
case 'm':
escape$m(params);
break;
case 's':
escape$s(params);
break;
case 'u':
escape$u(params);
break;
case 't':
escape$t(params);
break;
case 'r':
escape$r(params);
break;
case 'l':
escape$h_l(true, question_param, params);
break;
case 'h':
escape$h_l(false, question_param, params);
break;
default:
dbgprintf("Terminal::execute_escape_sequence: Unhandled final '%c'\n", final);
break;
}
#if defined(TERMINAL_DEBUG)
dbgprintf("\n");
for (auto& line : m_lines) {
dbgprintf("Terminal: Line: ");
for (int i = 0; i < line->length; i++) {
dbgprintf("%c", line->characters[i]);
}
dbgprintf("\n");
}
#endif
m_parameters.clear_with_capacity();
m_intermediates.clear_with_capacity();
}
@ -500,7 +701,7 @@ void Terminal::execute_escape_sequence(byte final)
void Terminal::newline()
{
word new_row = m_cursor_row;
if (m_cursor_row == (rows() - 1)) {
if (m_cursor_row == m_scroll_region_bottom) {
scroll_up();
} else {
++new_row;
@ -512,11 +713,17 @@ void Terminal::scroll_up()
{
// NOTE: We have to invalidate the cursor first.
invalidate_cursor();
delete m_lines[0];
for (word row = 1; row < rows(); ++row)
m_lines[row - 1] = m_lines[row];
m_lines[m_rows - 1] = new Line(m_columns);
++m_rows_to_scroll_backing_store;
m_lines.remove(m_scroll_region_top);
m_lines.insert(m_scroll_region_bottom, make<Line>(m_columns));
m_need_full_flush = true;
}
void Terminal::scroll_down()
{
// NOTE: We have to invalidate the cursor first.
invalidate_cursor();
m_lines.remove(m_scroll_region_bottom);
m_lines.insert(m_scroll_region_top, make<Line>(m_columns));
m_need_full_flush = true;
}
@ -531,7 +738,7 @@ void Terminal::set_cursor(unsigned a_row, unsigned a_column)
invalidate_cursor();
m_cursor_row = row;
m_cursor_column = column;
if (column != columns() - 1)
if (column != columns() - 1u)
m_stomp = false;
invalidate_cursor();
}
@ -541,11 +748,11 @@ void Terminal::put_character_at(unsigned row, unsigned column, byte ch)
ASSERT(row < rows());
ASSERT(column < columns());
auto& line = this->line(row);
if ((line.characters[column] == ch) && (line.attributes[column] == m_current_attribute))
return;
line.characters[column] = ch;
line.attributes[column] = m_current_attribute;
line.dirty = true;
m_last_char = ch;
}
void Terminal::on_char(byte ch)
@ -628,7 +835,16 @@ void Terminal::on_char(byte ch)
}
return;
case '\a':
sysbeep();
if (m_should_beep)
sysbeep();
else {
m_visual_beep_timer.restart(200);
m_visual_beep_timer.set_single_shot(true);
m_visual_beep_timer.on_timeout = [this] {
force_repaint();
};
force_repaint();
}
return;
case '\t': {
for (unsigned i = m_cursor_column; i < columns(); ++i) {
@ -700,39 +916,43 @@ void Terminal::set_size(word columns, word rows)
if (columns == m_columns && rows == m_rows)
return;
if (m_lines) {
for (size_t i = 0; i < m_rows; ++i)
delete m_lines[i];
delete m_lines;
#if defined(TERMINAL_DEBUG)
dbgprintf("Terminal: RESIZE to: %d rows\n", rows);
#endif
if (rows > m_rows) {
while (m_lines.size() < rows)
m_lines.append(make<Line>(columns));
} else {
m_lines.resize(rows);
}
for (int i = 0; i < rows; ++i)
m_lines[i]->set_length(columns);
m_columns = columns;
m_rows = rows;
m_scroll_region_top = 0;
m_scroll_region_bottom = rows - 1;
m_cursor_row = 0;
m_cursor_column = 0;
m_saved_cursor_row = 0;
m_saved_cursor_column = 0;
if (m_horizontal_tabs)
free(m_horizontal_tabs);
m_horizontal_tabs = static_cast<byte*>(malloc(columns));
m_horizontal_tabs.resize(columns);
for (unsigned i = 0; i < columns; ++i)
m_horizontal_tabs[i] = (i % 8) == 0;
// Rightmost column is always last tab on line.
m_horizontal_tabs[columns - 1] = 1;
m_lines = new Line*[rows];
for (size_t i = 0; i < rows; ++i)
m_lines[i] = new Line(columns);
m_pixel_width = (frame_thickness() * 2) + (m_inset * 2) + (m_columns * font().glyph_width('x'));
m_pixel_height = (frame_thickness() * 2) + (m_inset * 2) + (m_rows * (font().glyph_height() + m_line_spacing)) - m_line_spacing;
set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
set_preferred_size({ m_pixel_width, m_pixel_height });
m_rows_to_scroll_backing_store = 0;
m_needs_background_fill = true;
force_repaint();
@ -760,11 +980,11 @@ Rect Terminal::row_rect(word row)
bool Terminal::Line::has_only_one_background_color() const
{
if (!length)
if (!m_length)
return true;
// FIXME: Cache this result?
auto color = attributes[0].background_color;
for (size_t i = 1; i < length; ++i) {
for (size_t i = 1; i < m_length; ++i) {
if (attributes[i].background_color != color)
return false;
}
@ -816,6 +1036,11 @@ void Terminal::keydown_event(GKeyEvent& event)
case KeyCode::Key_End:
write(m_ptm_fd, "\033[F", 3);
break;
case KeyCode::Key_RightShift:
// Prevent RightShift from being sent to whatever's running in the
// terminal. Prevents `~@` (null) character from being sent after every
// character entered with right shift.
break;
default:
write(m_ptm_fd, &ch, 1);
break;
@ -823,45 +1048,28 @@ void Terminal::keydown_event(GKeyEvent& event)
}
void Terminal::paint_event(GPaintEvent& event)
{
{
GFrame::paint_event(event);
GPainter painter(*this);
if (m_needs_background_fill) {
m_needs_background_fill = false;
if (m_visual_beep_timer.is_active())
painter.fill_rect(frame_inner_rect(), Color::Red);
else
painter.fill_rect(frame_inner_rect(), Color(Color::Black).with_alpha(255 * m_opacity));
}
if (m_rows_to_scroll_backing_store && m_rows_to_scroll_backing_store < m_rows) {
int first_scanline = m_inset;
int second_scanline = m_inset + (m_rows_to_scroll_backing_store * m_line_height);
int num_rows_to_memcpy = m_rows - m_rows_to_scroll_backing_store;
int scanlines_to_copy = (num_rows_to_memcpy * m_line_height) - m_line_spacing;
memcpy(
painter.target()->scanline(first_scanline),
painter.target()->scanline(second_scanline),
scanlines_to_copy * painter.target()->pitch()
);
line(max(0, m_cursor_row - m_rows_to_scroll_backing_store)).dirty = true;
}
m_rows_to_scroll_backing_store = 0;
invalidate_cursor();
for (word row = 0; row < m_rows; ++row) {
auto& line = this->line(row);
if (!line.dirty)
continue;
line.dirty = false;
bool has_only_one_background_color = line.has_only_one_background_color();
if (has_only_one_background_color) {
if (m_visual_beep_timer.is_active())
painter.fill_rect(row_rect(row), Color::Red);
else if (has_only_one_background_color)
painter.fill_rect(row_rect(row), lookup_color(line.attributes[0].background_color).with_alpha(255 * m_opacity));
}
for (word column = 0; column < m_columns; ++column) {
char ch = line.characters[column];
bool should_reverse_fill_for_cursor = m_cursor_blink_state && m_in_active_window && row == m_cursor_row && column == m_cursor_column;
auto& attribute = line.attributes[column];
char ch = line.characters[column];
auto character_rect = glyph_rect(row, column);
if (!has_only_one_background_color || should_reverse_fill_for_cursor) {
auto cell_rect = character_rect.inflated(0, m_line_spacing);
@ -877,9 +1085,6 @@ void Terminal::paint_event(GPaintEvent& event)
auto cell_rect = glyph_rect(m_cursor_row, m_cursor_column).inflated(0, m_line_spacing);
painter.draw_rect(cell_rect, lookup_color(line(m_cursor_row).attributes[m_cursor_column].foreground_color));
}
if (m_belling)
painter.draw_rect(frame_inner_rect(), Color::Red);
}
void Terminal::set_window_title(const String& title)

View file

@ -26,6 +26,9 @@ public:
void apply_size_increments_to_window(GWindow&);
void set_opacity(float);
float opacity() { return m_opacity; };
bool should_beep() { return m_should_beep; }
void set_should_beep(bool sb) { m_should_beep = sb; };
RetainPtr<CConfigFile> config() const { return m_config; }
@ -39,6 +42,7 @@ private:
virtual const char* class_name() const override { return "Terminal"; }
void scroll_up();
void scroll_down();
void newline();
void set_cursor(unsigned row, unsigned column);
void put_character_at(unsigned row, unsigned column, byte ch);
@ -57,14 +61,20 @@ private:
void escape$J(const ParamVector&);
void escape$K(const ParamVector&);
void escape$M(const ParamVector&);
void escape$P(const ParamVector&);
void escape$G(const ParamVector&);
void escape$X(const ParamVector&);
void escape$b(const ParamVector&);
void escape$d(const ParamVector&);
void escape$m(const ParamVector&);
void escape$s(const ParamVector&);
void escape$u(const ParamVector&);
void escape$t(const ParamVector&);
void escape$r(const ParamVector&);
void escape$S(const ParamVector&);
void escape$T(const ParamVector&);
void escape$L(const ParamVector&);
void escape$h_l(bool, bool, const ParamVector&);
void clear();
@ -118,10 +128,11 @@ private:
~Line();
void clear(Attribute);
bool has_only_one_background_color() const;
void set_length(word);
byte* characters { nullptr };
Attribute* attributes { nullptr };
bool dirty { false };
word length { 0 };
word m_length { 0 };
};
Line& line(size_t index)
{
@ -129,7 +140,10 @@ private:
return *m_lines[index];
}
Line** m_lines { nullptr };
Vector<OwnPtr<Line>> m_lines;
int m_scroll_region_top { 0 };
int m_scroll_region_bottom { 0 };
word m_columns { 0 };
word m_rows { 0 };
@ -140,13 +154,14 @@ private:
byte m_saved_cursor_column { 0 };
bool m_stomp { false };
bool m_should_beep { false };
Attribute m_current_attribute;
void execute_escape_sequence(byte final);
void execute_xterm_command();
enum EscapeState
{
enum EscapeState {
Normal,
ExpectBracket,
ExpectParameter,
@ -162,13 +177,12 @@ private:
Vector<byte> m_intermediates;
Vector<byte> m_xterm_param1;
Vector<byte> m_xterm_param2;
Vector<bool> m_horizontal_tabs;
byte m_final { 0 };
byte* m_horizontal_tabs { nullptr };
bool m_belling { false };
int m_pixel_width { 0 };
int m_pixel_height { 0 };
int m_rows_to_scroll_backing_store { 0 };
int m_inset { 2 };
int m_line_spacing { 4 };
@ -190,5 +204,8 @@ private:
int m_glyph_width { 0 };
CTimer m_cursor_blink_timer;
CTimer m_visual_beep_timer;
RetainPtr<CConfigFile> m_config;
byte m_last_char { 0 };
};

View file

@ -1,23 +1,26 @@
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <pwd.h>
#include "Terminal.h"
#include <Kernel/KeyCode.h>
#include <LibCore/CUserInfo.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GFontDatabase.h>
#include <LibGUI/GGroupBox.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GRadioButton.h>
#include <LibGUI/GSlider.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GFontDatabase.h>
#include <LibGUI/GSlider.h>
#include <LibCore/CUserInfo.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>
static void make_shell(int ptm_fd)
{
@ -36,7 +39,7 @@ static void make_shell(int ptm_fd)
}
// NOTE: It's okay if this fails.
(void) ioctl(0, TIOCNOTTY);
(void)ioctl(0, TIOCNOTTY);
close(0);
close(1);
@ -78,6 +81,54 @@ static void make_shell(int ptm_fd)
}
}
GWindow* create_settings_window(Terminal& terminal, RetainPtr<CConfigFile> config)
{
auto* window = new GWindow;
window->set_title("Terminal Settings");
window->set_rect(50, 50, 200, 140);
auto* settings = new GWidget;
window->set_main_widget(settings);
settings->set_fill_with_background_color(true);
settings->set_layout(make<GBoxLayout>(Orientation::Vertical));
settings->layout()->set_margins({ 4, 4, 4, 4 });
auto* radio_container = new GGroupBox("Bell Mode", settings);
radio_container->set_layout(make<GBoxLayout>(Orientation::Vertical));
radio_container->layout()->set_margins({ 6, 16, 6, 6 });
radio_container->set_fill_with_background_color(true);
radio_container->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
radio_container->set_preferred_size({ 100, 70 });
auto* sysbell_radio = new GRadioButton("Use (Audible) System Bell", radio_container);
auto* visbell_radio = new GRadioButton("Use (Visual) Terminal Bell", radio_container);
sysbell_radio->set_checked(terminal.should_beep());
visbell_radio->set_checked(!terminal.should_beep());
sysbell_radio->on_checked = [&terminal](const bool checked) {
terminal.set_should_beep(checked);
};
auto* slider_container = new GGroupBox("Background Opacity", settings);
slider_container->set_layout(make<GBoxLayout>(Orientation::Vertical));
slider_container->layout()->set_margins({ 6, 16, 6, 6 });
slider_container->set_fill_with_background_color(true);
slider_container->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
slider_container->set_preferred_size({ 100, 50 });
auto* slider = new GSlider(slider_container);
slider->set_fill_with_background_color(true);
slider->set_background_color(Color::LightGray);
slider->on_value_changed = [&terminal, &config](int value) {
float opacity = value / 100.0;
terminal.set_opacity(opacity);
};
slider->set_range(0, 100);
slider->set_value(terminal.opacity() * 100.0);
return window;
}
int main(int argc, char** argv)
{
GApplication app(argc, argv);
@ -106,23 +157,9 @@ int main(int argc, char** argv)
terminal.apply_size_increments_to_window(*window);
window->show();
window->set_icon_path("/res/icons/16x16/app-terminal.png");
terminal.set_should_beep(config->read_bool_entry("Window", "AudibleBeep", false));
auto* opacity_adjustment_window = new GWindow;
opacity_adjustment_window->set_title("Adjust opacity");
opacity_adjustment_window->set_rect(50, 50, 200, 100);
auto* slider = new GSlider(nullptr);
opacity_adjustment_window->set_main_widget(slider);
slider->set_fill_with_background_color(true);
slider->set_background_color(Color::LightGray);
slider->on_value_changed = [&terminal, &config] (int value) {
float opacity = value / 100.0;
terminal.set_opacity(opacity);
};
slider->set_range(0, 100);
slider->set_value(100);
WeakPtr<GWindow> settings_window;
auto new_opacity = config->read_num_entry("Window", "Opacity", 255);
terminal.set_opacity((float)new_opacity / 255.0);
@ -130,10 +167,14 @@ int main(int argc, char** argv)
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("Terminal");
app_menu->add_action(GAction::create("Adjust opacity...", [opacity_adjustment_window] (const GAction&) {
opacity_adjustment_window->show();
}));
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [] (const GAction&) {
app_menu->add_action(GAction::create("Settings...",
[&settings_window, &terminal, &config](const GAction&) {
if (!settings_window)
settings_window = create_settings_window(terminal, config)->make_weak_ptr();
settings_window->show();
settings_window->move_to_front();
}));
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [](const GAction&) {
dbgprintf("Terminal: Quit menu activated!\n");
GApplication::the().quit(0);
return;
@ -141,8 +182,8 @@ int main(int argc, char** argv)
menubar->add_menu(move(app_menu));
auto font_menu = make<GMenu>("Font");
GFontDatabase::the().for_each_fixed_width_font([&] (const String& font_name) {
font_menu->add_action(GAction::create(font_name, [&terminal, &config] (const GAction& action) {
GFontDatabase::the().for_each_fixed_width_font([&](const StringView& font_name) {
font_menu->add_action(GAction::create(font_name, [&terminal, &config](const GAction& action) {
terminal.set_font(GFontDatabase::the().get_by_name(action.text()));
auto metadata = GFontDatabase::the().get_metadata_by_name(action.text());
config->write_entry("Text", "Font", metadata.path);
@ -153,7 +194,7 @@ int main(int argc, char** argv)
menubar->add_menu(move(font_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(GAction::create("About", [] (const GAction&) {
help_menu->add_action(GAction::create("About", [](const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));

View file

@ -1,21 +1,21 @@
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GBoxLayout.h>
#include <AK/StringBuilder.h>
#include <LibCore/CFile.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GFilePicker.h>
#include <LibGUI/GFontDatabase.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GMessageBox.h>
#include <LibGUI/GStatusBar.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GTextEditor.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GFontDatabase.h>
#include <LibCore/CFile.h>
#include <AK/StringBuilder.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void open_sesame(GWindow& window, GTextEditor& editor, const String& path)
{
@ -57,7 +57,7 @@ int main(int argc, char** argv)
open_sesame(*window, *text_editor, path);
}
auto new_action = GAction::create("New document", { Mod_Ctrl, Key_N }, GraphicsBitmap::load_from_file("/res/icons/16x16/new.png"), [] (const GAction&) {
auto new_action = GAction::create("New document", { Mod_Ctrl, Key_N }, GraphicsBitmap::load_from_file("/res/icons/16x16/new.png"), [](const GAction&) {
dbgprintf("FIXME: Implement File/New\n");
});
@ -69,14 +69,14 @@ int main(int argc, char** argv)
}
});
auto save_action = GAction::create("Save document", { Mod_Ctrl, Key_S }, GraphicsBitmap::load_from_file("/res/icons/16x16/save.png"), [&] (const GAction&) {
auto save_action = GAction::create("Save document", { Mod_Ctrl, Key_S }, GraphicsBitmap::load_from_file("/res/icons/16x16/save.png"), [&](const GAction&) {
dbgprintf("Writing document to '%s'\n", path.characters());
text_editor->write_to_file(path);
});
auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("Text Editor");
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [] (const GAction&) {
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [](const GAction&) {
GApplication::the().quit(0);
return;
}));
@ -99,8 +99,8 @@ int main(int argc, char** argv)
menubar->add_menu(move(edit_menu));
auto font_menu = make<GMenu>("Font");
GFontDatabase::the().for_each_fixed_width_font([&] (const String& font_name) {
font_menu->add_action(GAction::create(font_name, [text_editor] (const GAction& action) {
GFontDatabase::the().for_each_fixed_width_font([&](const StringView& font_name) {
font_menu->add_action(GAction::create(font_name, [text_editor](const GAction& action) {
text_editor->set_font(GFontDatabase::the().get_by_name(action.text()));
text_editor->update();
}));
@ -108,7 +108,7 @@ int main(int argc, char** argv)
menubar->add_menu(move(font_menu));
auto help_menu = make<GMenu>("Help");
help_menu->add_action(GAction::create("About", [] (const GAction&) {
help_menu->add_action(GAction::create("About", [](const GAction&) {
dbgprintf("FIXME: Implement Help/About\n");
}));
menubar->add_menu(move(help_menu));

View file

@ -1,3 +1,2 @@
[DNS]
IPAddress=8.8.8.8

1
Base/etc/hosts Normal file
View file

@ -0,0 +1 @@
127.0.0.1 localhost

View file

@ -1,2 +1,3 @@
[Window]
Opacity=255
AudibleBeep=0

View file

@ -1,5 +1,5 @@
[Screen]
Width=1080
Width=1024
Height=768
[Cursor]
@ -36,4 +36,4 @@ MenuSelectionColor=132,53,26
DoubleClickSpeed=250
[Background]
Mode=center
Mode=scaled

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

3
Demos/Fire/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
Fire
*.o
*.d

236
Demos/Fire/Fire.cpp Normal file
View file

@ -0,0 +1,236 @@
/* Fire.cpp - a (classic) graphics demo for Serenity, by pd.
* heavily based on the Fabien Sanglard's article:
* http://fabiensanglard.net/doom_fire_psx/index.html
*
* Future directions:
* [X] This does suggest the need for a palletized graphics surface. Thanks kling!
* [X] alternate column updates, or vertical interlacing. this would certainly alter
* the effect, but the update load would be halved.
* [/] scaled blit
* [ ] dithering?
* [X] inlining rand()
* [/] precalculating and recycling random data
* [ ] rework/expand palette
* [ ] switch to use tsc values for perf check
* [ ] handle mouse events differently for smoother painting (queue)
* [ ] handle fire bitmap edges better
*/
#include <LibGUI/GApplication.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GPainter.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
#include <SharedGraphics/GraphicsBitmap.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define FIRE_WIDTH 320
#define FIRE_HEIGHT 168
#define FIRE_MAX 29
const Color palette[] = {
Color(0x07, 0x07, 0x07), Color(0x1F, 0x07, 0x07), Color(0x2F, 0x0F, 0x07),
Color(0x47, 0x0F, 0x07), Color(0x57, 0x17, 0x07), Color(0x67, 0x1F, 0x07),
Color(0x77, 0x1F, 0x07), Color(0x9F, 0x2F, 0x07), Color(0xAF, 0x3F, 0x07),
Color(0xBF, 0x47, 0x07), Color(0xC7, 0x47, 0x07), Color(0xDF, 0x4F, 0x07),
Color(0xDF, 0x57, 0x07), Color(0xD7, 0x5F, 0x07), Color(0xD7, 0x5F, 0x07),
Color(0xD7, 0x67, 0x0F), Color(0xCF, 0x6F, 0x0F), Color(0xCF, 0x7F, 0x0F),
Color(0xCF, 0x87, 0x17), Color(0xC7, 0x87, 0x17), Color(0xC7, 0x8F, 0x17),
Color(0xC7, 0x97, 0x1F), Color(0xBF, 0x9F, 0x1F), Color(0xBF, 0xA7, 0x27),
Color(0xBF, 0xAF, 0x2F), Color(0xB7, 0xAF, 0x2F), Color(0xB7, 0xB7, 0x37),
Color(0xCF, 0xCF, 0x6F), Color(0xEF, 0xEF, 0xC7), Color(0xFF, 0xFF, 0xFF)
};
/* Random functions...
* These are from musl libc's prng/rand.c
*/
static uint64_t seed;
void my_srand(unsigned s)
{
seed = s - 1;
}
static int my_rand(void)
{
seed = 6364136223846793005ULL * seed + 1;
return seed >> 33;
}
/*
* Fire Widget
*/
class Fire : public GWidget {
public:
explicit Fire(GWidget* parent = nullptr);
virtual ~Fire() override;
void set_stat_label(GLabel* l) { stats = l; };
private:
RetainPtr<GraphicsBitmap> bitmap;
GLabel* stats;
virtual void paint_event(GPaintEvent&) override;
virtual void timer_event(CTimerEvent&) override;
virtual void mousedown_event(GMouseEvent& event) override;
virtual void mousemove_event(GMouseEvent& event) override;
virtual void mouseup_event(GMouseEvent& event) override;
bool dragging;
int timeAvg;
int cycles;
int phase;
};
Fire::Fire(GWidget* parent)
: GWidget(parent)
{
bitmap = GraphicsBitmap::create(GraphicsBitmap::Format::Indexed8, { 320, 200 });
/* Initialize fire palette */
for (int i = 0; i < 30; i++)
bitmap->set_palette_color(i, palette[i]);
/* Set remaining entries to white */
for (int i = 30; i < 256; i++)
bitmap->set_palette_color(i, Color::White);
dragging = false;
timeAvg = 0;
cycles = 0;
phase = 0;
my_srand(time(nullptr));
stop_timer();
start_timer(20);
/* Draw fire "source" on bottom row of pixels */
for (int i = 0; i < FIRE_WIDTH; i++)
bitmap->bits(bitmap->height() - 1)[i] = FIRE_MAX;
/* Set off initital paint event */
//update();
}
Fire::~Fire()
{
}
void Fire::paint_event(GPaintEvent& event)
{
CElapsedTimer timer;
timer.start();
GPainter painter(*this);
painter.add_clip_rect(event.rect());
/* Blit it! */
painter.draw_scaled_bitmap(event.rect(), *bitmap, bitmap->rect());
timeAvg += timer.elapsed();
cycles++;
}
void Fire::timer_event(CTimerEvent&)
{
/* Update only even or odd columns per frame... */
phase++;
if (phase > 1)
phase = 0;
/* Paint our palettized buffer to screen */
for (int px = 0 + phase; px < FIRE_WIDTH; px += 2) {
for (int py = 1; py < 200; py++) {
int rnd = my_rand() % 3;
/* Calculate new pixel value, don't go below 0 */
byte nv = bitmap->bits(py)[px];
if (nv > 0)
nv -= (rnd & 1);
/* ...sigh... */
int epx = px + (1 - rnd);
if (epx < 0)
epx = 0;
else if (epx > FIRE_WIDTH)
epx = FIRE_WIDTH;
bitmap->bits(py - 1)[epx] = nv;
}
}
if ((cycles % 50) == 0) {
dbgprintf("%d total cycles. finished 50 in %d ms, avg %d ms\n", cycles, timeAvg, timeAvg / 50);
stats->set_text(String::format("%d ms", timeAvg / 50));
timeAvg = 0;
}
update();
}
/*
* Mouse handling events
*/
void Fire::mousedown_event(GMouseEvent& event)
{
if (event.button() == GMouseButton::Left)
dragging = true;
return GWidget::mousedown_event(event);
}
/* FIXME: needs to account for the size of the window rect */
void Fire::mousemove_event(GMouseEvent& event)
{
if (dragging) {
if (event.y() >= 2 && event.y() < 398 && event.x() <= 638) {
int ypos = event.y() / 2;
int xpos = event.x() / 2;
bitmap->bits(ypos - 1)[xpos] = FIRE_MAX + 5;
bitmap->bits(ypos - 1)[xpos + 1] = FIRE_MAX + 5;
bitmap->bits(ypos)[xpos] = FIRE_MAX + 5;
bitmap->bits(ypos)[xpos + 1] = FIRE_MAX + 5;
}
}
return GWidget::mousemove_event(event);
}
void Fire::mouseup_event(GMouseEvent& event)
{
if (event.button() == GMouseButton::Left)
dragging = false;
return GWidget::mouseup_event(event);
}
/*
* Main
*/
int main(int argc, char** argv)
{
GApplication app(argc, argv);
auto* window = new GWindow;
window->set_should_exit_event_loop_on_close(true);
window->set_double_buffering_enabled(false);
window->set_title("Fire");
window->set_resizable(false);
window->set_rect(100, 100, 640, 400);
auto* fire = new Fire;
window->set_main_widget(fire);
auto* time = new GLabel(fire);
time->set_relative_rect({ 0, 4, 40, 10 });
time->move_by({ window->width() - time->width(), 0 });
time->set_foreground_color(Color::from_rgb(0x444444));
fire->set_stat_label(time);
window->show();
window->set_icon_path("/res/icons/16x16/app-demo.png");
return app.exec();
}

22
Demos/Fire/Makefile Normal file
View file

@ -0,0 +1,22 @@
include ../../Makefile.common
OBJS = \
Fire.o
APP = Fire
DEFINES += -DUSERLAND
all: $(APP)
$(APP): $(OBJS)
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lgui -lcore -lc
.cpp.o:
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
-include $(OBJS:%.o=%.d)
clean:
@echo "CLEAN"; rm -f $(APP) $(OBJS) *.d

View file

@ -1,9 +1,9 @@
#include <LibGUI/GApplication.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GWindow.h>
int main(int argc, char** argv)
{
@ -27,7 +27,7 @@ int main(int argc, char** argv)
button->set_text("Good-bye");
button->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
button->set_preferred_size({ 0, 20 });
button->on_click = [&] (GButton&) {
button->on_click = [&](GButton&) {
app.quit();
};

Some files were not shown because too many files have changed in this diff Show more