1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 17:15:06 +00:00

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
527 changed files with 9637 additions and 5614 deletions

View file

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

2
.gitignore vendored
View file

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

View file

@ -16,7 +16,7 @@ notifications:
before_install: before_install:
- sudo apt-get update - sudo apt-get update
- sudo apt-get install -y libmpfr-dev libmpc-dev libgmp-dev - 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: script:
- cd Toolchain - cd Toolchain

View file

@ -10,15 +10,38 @@
namespace AK { 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 { class String {
public: public:
~String() {} ~String() {}
String() {} String() {}
String(StringView view) String(const StringView& view)
: m_impl(StringImpl::create(view.characters(), view.length()))
{ {
if (view.m_impl)
m_impl = *view.m_impl;
else
m_impl = StringImpl::create(view.characters(), view.length());
} }
String(const String& other) 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)) : m_impl(StringImpl::create(cstring, length, shouldChomp))
{ {
} }
@ -67,7 +90,7 @@ public:
}; };
static String repeated(char, int count); 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; int to_int(bool& ok) const;
unsigned to_uint(bool& ok) const; unsigned to_uint(bool& ok) const;
@ -86,6 +109,7 @@ public:
return m_impl->to_uppercase(); return m_impl->to_uppercase();
} }
Vector<String> split_limit(char separator, int limit) const;
Vector<String> split(char separator) const; Vector<String> split(char separator) const;
String substring(int start, int length) const; String substring(int start, int length) const;
@ -94,20 +118,35 @@ public:
bool is_null() const { return !m_impl; } bool is_null() const { return !m_impl; }
bool is_empty() const { return length() == 0; } 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; } 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); ASSERT(m_impl);
return (*m_impl)[i]; 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&) const;
bool operator!=(const String& other) const { return !(*this == other); } bool operator!=(const String& other) const { return !(*this == other); }
bool operator<(const String&) const; 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; String isolated_copy() const;
static String empty(); static String empty();
@ -146,7 +185,7 @@ public:
StringView view() const { return { characters(), length() }; } StringView view() const { return { characters(), length() }; }
private: private:
bool match_helper(const String& mask) const; bool match_helper(const StringView& mask) const;
RetainPtr<StringImpl> m_impl; RetainPtr<StringImpl> m_impl;
}; };

View file

@ -1,7 +1,20 @@
#pragma once #pragma once
namespace AK {
template<typename T> template<typename T>
class Badge { class Badge {
friend T; friend T;
Badge() {} 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); return Bitmap(size, default_value);
} }
static Bitmap create()
{
return Bitmap();
}
~Bitmap() ~Bitmap()
{ {
if (m_owned) if (m_owned)
@ -45,12 +50,74 @@ public:
byte* data() { return m_data; } byte* data() { return m_data; }
const byte* data() const { 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) void fill(bool value)
{ {
memset(m_data, value ? 0xff : 0x00, size_in_bytes()); 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: private:
explicit Bitmap()
: m_size(0)
, m_owned(true)
{
m_data = nullptr;
}
explicit Bitmap(int size, bool default_value) explicit Bitmap(int size, bool default_value)
: m_size(size) : m_size(size)
, m_owned(true) , m_owned(true)

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
#include "ELFLoader.h" #include "ELFLoader.h"
#include <AK/kstdio.h>
#include <AK/QuickSort.h> #include <AK/QuickSort.h>
#include <AK/kstdio.h>
//#define ELFLOADER_DEBUG //#define ELFLOADER_DEBUG
@ -30,33 +30,31 @@ bool ELFLoader::load()
bool ELFLoader::layout() bool ELFLoader::layout()
{ {
bool failed = false; 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) if (program_header.type() != PT_LOAD)
return; return;
#ifdef ELFLOADER_DEBUG #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 #endif
if (program_header.is_writable()) { if (program_header.is_writable()) {
alloc_section_hook( alloc_section_hook(
program_header.laddr(), program_header.vaddr(),
program_header.size_in_memory(), program_header.size_in_memory(),
program_header.alignment(), program_header.alignment(),
program_header.is_readable(), program_header.is_readable(),
program_header.is_writable(), program_header.is_writable(),
String::format("elf-alloc-%s%s", program_header.is_readable() ? "r" : "", program_header.is_writable() ? "w" : "") 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());
memcpy(program_header.laddr().as_ptr(), program_header.raw_data(), program_header.size_in_image());
} else { } else {
map_section_hook( map_section_hook(
program_header.laddr(), program_header.vaddr(),
program_header.size_in_memory(), program_header.size_in_memory(),
program_header.alignment(), program_header.alignment(),
program_header.offset(), program_header.offset(),
program_header.is_readable(), program_header.is_readable(),
program_header.is_writable(), program_header.is_writable(),
program_header.is_executable(), 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; return !failed;
@ -65,7 +63,7 @@ bool ELFLoader::layout()
char* ELFLoader::symbol_ptr(const char* name) char* ELFLoader::symbol_ptr(const char* name)
{ {
char* found_ptr = nullptr; 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) if (symbol.type() != STT_FUNC)
return IterationDecision::Continue; return IterationDecision::Continue;
if (strcmp(symbol.name(), name)) if (strcmp(symbol.name(), name))
@ -74,7 +72,7 @@ char* ELFLoader::symbol_ptr(const char* name)
found_ptr = (char*)symbol.value(); found_ptr = (char*)symbol.value();
else else
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
return IterationDecision::Abort; return IterationDecision::Break;
}); });
return found_ptr; return found_ptr;
} }
@ -83,11 +81,11 @@ String ELFLoader::symbolicate(dword address) const
{ {
if (m_sorted_symbols.is_empty()) { if (m_sorted_symbols.is_empty()) {
m_sorted_symbols.ensure_capacity(m_image.symbol_count()); 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() }); m_sorted_symbols.append({ symbol.value(), symbol.name() });
return IterationDecision::Continue; 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; return a.address < b.address;
}); });
} }

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -7,12 +7,12 @@ namespace AK {
class FileSystemPath { class FileSystemPath {
public: public:
FileSystemPath() {} FileSystemPath() {}
explicit FileSystemPath(const String&); explicit FileSystemPath(const StringView&);
bool is_valid() const { return m_is_valid; } 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; } 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 { namespace AK {
MappedFile::MappedFile(const String& file_name) MappedFile::MappedFile(const StringView& file_name)
: m_file_name(file_name)
{ {
m_size = PAGE_SIZE; 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) { if (m_fd != -1) {
struct stat st; struct stat st;
@ -26,7 +25,7 @@ MappedFile::MappedFile(const String& file_name)
} }
#ifdef DEBUG_MAPPED_FILE #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 #endif
} }
@ -44,15 +43,13 @@ void MappedFile::unmap()
ASSERT(rc == 0); ASSERT(rc == 0);
rc = close(m_fd); rc = close(m_fd);
ASSERT(rc == 0); ASSERT(rc == 0);
m_file_name = {};
m_size = 0; m_size = 0;
m_fd = -1; m_fd = -1;
m_map = (void*)-1; m_map = (void*)-1;
} }
MappedFile::MappedFile(MappedFile&& other) 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_fd(other.m_fd)
, m_map(other.m_map) , m_map(other.m_map)
{ {
@ -66,7 +63,6 @@ MappedFile& MappedFile::operator=(MappedFile&& other)
if (this == &other) if (this == &other)
return *this; return *this;
unmap(); unmap();
swap(m_file_name, other.m_file_name);
swap(m_size, other.m_size); swap(m_size, other.m_size);
swap(m_fd, other.m_fd); swap(m_fd, other.m_fd);
swap(m_map, other.m_map); swap(m_map, other.m_map);

View file

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

View file

@ -1,17 +1,15 @@
typedef unsigned char byte; #pragma once
typedef unsigned short word;
typedef unsigned int dword;
typedef long long unsigned int qword;
[[gnu::always_inline]] inline size_t strlen(const char* str) #include <AK/Types.h>
{ #include <stdarg.h>
size_t len = 0;
while (*(str++))
++len;
return len;
}
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> template<typename PutChFunc, typename T>
[[gnu::always_inline]] inline int print_hex(PutChFunc putch, char*& bufptr, T number, byte fields) [[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; byte shr_count = fields * 4;
while (shr_count) { while (shr_count) {
shr_count -= 4; shr_count -= 4;
putch(bufptr, h[(number >> shr_count) & 0x0F]); putch(bufptr, printf_hex_digits[(number >> shr_count) & 0x0F]);
++ret; ++ret;
} }
return ret; return ret;
@ -180,7 +178,7 @@ template<typename PutChFunc>
} }
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; 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 #pragma once
#include <AK/StdLibExtras.h>
namespace AK { namespace AK {
template<typename T> template<typename T>

View file

@ -103,20 +103,41 @@ public:
return *this; 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); 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); retain_if_not_null(m_ptr);
return *this; 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) if (m_ptr != &object)
release_if_not_null(m_ptr); release_if_not_null(m_ptr);
m_ptr = &object; m_ptr = const_cast<T*>(&object);
retain_if_not_null(m_ptr); retain_if_not_null(m_ptr);
return *this; return *this;
} }

View file

@ -44,16 +44,10 @@ public:
{ {
m_ptr->retain(); m_ptr->retain();
} }
RETURN_TYPESTATE(unconsumed)
Retained(T& object)
: m_ptr(&object)
{
m_ptr->retain();
}
template<typename U> template<typename U>
RETURN_TYPESTATE(unconsumed) RETURN_TYPESTATE(unconsumed)
Retained(U& object) Retained(const U& object)
: m_ptr(&static_cast<T&>(object)) : m_ptr(&const_cast<T&>(static_cast<const T&>(object)))
{ {
m_ptr->retain(); m_ptr->retain();
} }
@ -200,6 +194,19 @@ public:
return m_ptr; 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: private:
Retained() {} 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: private:
struct Node { struct Node {
explicit Node(T&& v) explicit Node(T&& v)
: value(v) : value(move(v))
{ {
} }
T value; T value;
@ -66,7 +66,7 @@ public:
{ {
ASSERT(m_head); ASSERT(m_head);
auto* prev_head = m_head; auto* prev_head = m_head;
T value = first(); T value = move(first());
if (m_tail == m_head) if (m_tail == m_head)
m_tail = nullptr; m_tail = nullptr;
m_head = m_head->next; m_head = m_head->next;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,13 @@
namespace AK { 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 Vector<StringView> StringView::split_view(const char separator) const
{ {
if (is_empty()) if (is_empty())
@ -23,7 +30,7 @@ Vector<StringView> StringView::split_view(const char separator) const
if (taillen != 0) if (taillen != 0)
v.append(substring_view(substart, taillen)); v.append(substring_view(substart, taillen));
if (characters()[length() - 1] == separator) if (characters()[length() - 1] == separator)
v.append(String::empty().view()); v.append(String::empty());
return v; return v;
} }
@ -35,6 +42,24 @@ StringView StringView::substring_view(int start, int length) const
return { m_characters + start, length }; 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 StringView::to_uint(bool& ok) const
{ {
unsigned value = 0; unsigned value = 0;

View file

@ -5,6 +5,7 @@
namespace AK { namespace AK {
class String; class String;
class StringImpl;
class StringView { class StringView {
public: public:
@ -27,7 +28,9 @@ public:
++m_length; ++m_length;
} }
} }
StringView(const String& string);
bool is_null() const { return !m_characters; }
bool is_empty() const { return m_length == 0; } bool is_empty() const { return m_length == 0; }
const char* characters() const { return m_characters; } const char* characters() const { return m_characters; }
int length() const { return m_length; } int length() const { return m_length; }
@ -37,12 +40,46 @@ public:
Vector<StringView> split_view(char) const; Vector<StringView> split_view(char) const;
unsigned to_uint(bool& ok) const; unsigned to_uint(bool& ok) const;
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
bool operator!=(const char* cstring) const { return strcmp(m_characters, cstring); } // 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; bool operator==(const String&) const;
private: private:
friend class String;
const StringImpl* m_impl { nullptr };
const char* m_characters { nullptr }; const char* m_characters { nullptr };
int m_length { 0 }; 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 { namespace AK {
template<typename TimevalType> 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_sec = a.tv_sec - b.tv_sec;
result->tv_usec = a->tv_usec - b->tv_usec; result.tv_usec = a.tv_usec - b.tv_usec;
if (result->tv_usec < 0) { if (result.tv_usec < 0) {
--result->tv_sec; --result.tv_sec;
result->tv_usec += 1000000; result.tv_usec += 1000000;
} }
} }
template<typename TimevalType> 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_sec = a.tv_sec + b.tv_sec;
result->tv_usec = a->tv_usec + b->tv_usec; result.tv_usec = a.tv_usec + b.tv_usec;
if (result->tv_usec > 1000000) { if (result.tv_usec > 1000000) {
++result->tv_sec; ++result.tv_sec;
result->tv_usec -= 1000000; result.tv_usec -= 1000000;
} }
} }
} }
using AK::timeval_add;
using AK::timeval_sub;

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <AK/IterationDecision.h>
#ifdef __serenity__ #ifdef __serenity__
typedef unsigned char byte; typedef unsigned char byte;
typedef unsigned short word; typedef unsigned short word;
@ -48,11 +50,6 @@ constexpr unsigned KB = 1024;
constexpr unsigned MB = KB * KB; constexpr unsigned MB = KB * KB;
constexpr unsigned GB = KB * KB * KB; constexpr unsigned GB = KB * KB * KB;
enum class IterationDecision {
Continue,
Abort
};
namespace std { namespace std {
typedef decltype(nullptr) nullptr_t; 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/StdLibExtras.h>
#include <AK/kmalloc.h> #include <AK/kmalloc.h>
#ifndef __serenity__
#include <new>
#endif
namespace AK { namespace AK {
template<typename T, int inline_capacity = 0> template<typename T, int inline_capacity = 0>
@ -294,6 +298,19 @@ public:
m_capacity = new_capacity; 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) void resize(int new_size)
{ {
if (new_size == size()) if (new_size == size())

View file

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

View file

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

View file

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

View file

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

View file

@ -1,25 +1,25 @@
#include <LibGUI/GWindow.h> #include "DirectoryView.h"
#include <LibGUI/GWidget.h> #include <AK/FileSystemPath.h>
#include <LibGUI/GBoxLayout.h> #include <LibCore/CUserInfo.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.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/GStatusBar.h>
#include <LibGUI/GTextEditor.h> #include <LibGUI/GTextEditor.h>
#include <LibGUI/GToolBar.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/GTreeView.h>
#include <LibGUI/GFileSystemModel.h> #include <LibGUI/GWidget.h>
#include <LibGUI/GSplitter.h> #include <LibGUI/GWindow.h>
#include <LibCore/CUserInfo.h>
#include <AK/FileSystemPath.h>
#include <unistd.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include "DirectoryView.h" #include <unistd.h>
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
@ -76,24 +76,24 @@ int main(int argc, char** argv)
directory_view->open(location_textbox->text()); 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); auto path = file_system_model->path(index);
if (directory_view->path() == path) if (directory_view->path() == path)
return; return;
directory_view->open(path); 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(); 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); GInputBox input_box("Enter name:", "New directory", window);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty()) { if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty()) {
auto new_dir_path = FileSystemPath(String::format("%s/%s", auto new_dir_path = FileSystemPath(String::format("%s/%s",
directory_view->path().characters(), directory_view->path().characters(),
input_box.text_value().characters() input_box.text_value().characters()))
)).string(); .string();
int rc = mkdir(new_dir_path.characters(), 0777); int rc = mkdir(new_dir_path.characters(), 0777);
if (rc < 0) { if (rc < 0) {
GMessageBox::show(String::format("mkdir(\"%s\") failed: %s", new_dir_path.characters(), strerror(errno)), "Error", GMessageBox::Type::Error, window); 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_table_action;
RetainPtr<GAction> view_as_icons_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); directory_view->set_view_mode(DirectoryView::ViewMode::List);
view_as_icons_action->set_checked(false); view_as_icons_action->set_checked(false);
view_as_table_action->set_checked(true); 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_checkable(true);
view_as_table_action->set_checked(false); 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); directory_view->set_view_mode(DirectoryView::ViewMode::Icon);
view_as_table_action->set_checked(false); view_as_table_action->set_checked(false);
view_as_icons_action->set_checked(true); 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_checkable(true);
view_as_icons_action->set_checked(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"); 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"); 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"); dbgprintf("'Go Back' action activated!\n");
directory_view->open_previous_directory(); 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"); dbgprintf("'Go Forward' action activated!\n");
directory_view->open_next_directory(); directory_view->open_next_directory();
}); });
@ -143,7 +143,7 @@ int main(int argc, char** argv)
auto menubar = make<GMenuBar>(); auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("File Manager"); 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); GApplication::the().quit(0);
return; return;
})); }));
@ -167,7 +167,7 @@ int main(int argc, char** argv)
menubar->add_menu(move(go_menu)); menubar->add_menu(move(go_menu));
auto help_menu = make<GMenu>("Help"); 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"); dbgprintf("FIXME: Implement Help/About\n");
})); }));
menubar->add_menu(move(help_menu)); 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_icons_action);
main_toolbar->add_action(*view_as_table_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())); window->set_title(String::format("File Manager: %s", new_path.characters()));
location_textbox->set_text(new_path); location_textbox->set_text(new_path);
file_system_model->set_selected_index(file_system_model->index(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(); tree_view->update();
go_forward_action->set_enabled(directory_view->path_history_position() 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); go_back_action->set_enabled(directory_view->path_history_position() > 0);
}; };
directory_view->on_status_message = [statusbar] (String message) { directory_view->on_status_message = [statusbar](const StringView& message) {
statusbar->set_text(move(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) { if (done == total) {
progressbar->set_visible(false); progressbar->set_visible(false);
return; return;

View file

@ -1,13 +1,13 @@
#include "FontEditor.h" #include "FontEditor.h"
#include "GlyphMapWidget.h"
#include "GlyphEditorWidget.h" #include "GlyphEditorWidget.h"
#include <LibGUI/GPainter.h> #include "GlyphMapWidget.h"
#include <LibGUI/GButton.h> #include <LibGUI/GButton.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GTextBox.h>
#include <LibGUI/GCheckBox.h> #include <LibGUI/GCheckBox.h>
#include <LibGUI/GSpinBox.h>
#include <LibGUI/GGroupBox.h> #include <LibGUI/GGroupBox.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GPainter.h>
#include <LibGUI/GSpinBox.h>
#include <LibGUI/GTextBox.h>
#include <stdlib.h> #include <stdlib.h>
FontEditorWidget::FontEditorWidget(const String& path, RetainPtr<Font>&& edited_font, GWidget* parent) 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); auto* save_button = new GButton(this);
save_button->set_text("Save"); save_button->set_text("Save");
save_button->set_relative_rect({ 5, 300, 105, 20 }); 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()); dbgprintf("write to file: '%s'\n", m_path.characters());
m_edited_font->write_to_file(m_path); 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); auto* quit_button = new GButton(this);
quit_button->set_text("Quit"); quit_button->set_text("Quit");
quit_button->set_relative_rect({ 110, 300, 105, 20 }); quit_button->set_relative_rect({ 110, 300, 105, 20 });
quit_button->on_click = [] (GButton&) { quit_button->on_click = [](GButton&) {
exit(0); exit(0);
}; };
@ -91,25 +91,25 @@ FontEditorWidget::FontEditorWidget(const String& path, RetainPtr<Font>&& edited_
demo_label_2->update(); 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); m_glyph_map_widget->update_glyph(glyph);
update_demo(); 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); m_glyph_editor_widget->set_glyph(glyph);
width_spinbox->set_value(m_edited_font->glyph_width(m_glyph_map_widget->selected_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)); 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); m_edited_font->set_fixed_width(checked);
width_spinbox->set_value(m_edited_font->glyph_width(m_glyph_map_widget->selected_glyph())); width_spinbox->set_value(m_edited_font->glyph_width(m_glyph_map_widget->selected_glyph()));
m_glyph_editor_widget->update(); m_glyph_editor_widget->update();
update_demo(); 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_edited_font->set_glyph_width(m_glyph_map_widget->selected_glyph(), value);
m_glyph_editor_widget->update(); m_glyph_editor_widget->update();
m_glyph_map_widget->update_glyph(m_glyph_map_widget->selected_glyph()); 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, row * (font().glyph_height() + m_vertical_spacing) + 1,
font().max_glyph_width() + m_horizontal_spacing, font().max_glyph_width() + m_horizontal_spacing,
font().glyph_height() + 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) 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.x() + m_horizontal_spacing / 2,
outer_rect.y() + m_vertical_spacing / 2, outer_rect.y() + m_vertical_spacing / 2,
font().max_glyph_width(), font().max_glyph_width(),
font().glyph_height() font().glyph_height());
);
if (glyph == m_selected_glyph) { if (glyph == m_selected_glyph) {
painter.fill_rect(outer_rect, Color::from_rgb(0x84351a)); painter.fill_rect(outer_rect, Color::from_rgb(0x84351a));
painter.draw_glyph(inner_rect.location(), glyph, Color::White); painter.draw_glyph(inner_rect.location(), glyph, Color::White);

View file

@ -1,16 +1,16 @@
#include "IRCAppWindow.h" #include "IRCAppWindow.h"
#include "IRCWindow.h" #include "IRCWindow.h"
#include "IRCWindowListModel.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/GAction.h>
#include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GInputBox.h>
#include <LibGUI/GMenu.h> #include <LibGUI/GMenu.h>
#include <LibGUI/GMenuBar.h> #include <LibGUI/GMenuBar.h>
#include <LibGUI/GInputBox.h>
#include <LibGUI/GSplitter.h> #include <LibGUI/GSplitter.h>
#include <LibGUI/GStackWidget.h>
#include <LibGUI/GTableView.h>
#include <LibGUI/GToolBar.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -36,7 +36,7 @@ void IRCAppWindow::update_title()
void IRCAppWindow::setup_client() 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); return &create_window(owner, type, name);
}; };
m_client.aid_get_active_window = [this] { m_client.aid_get_active_window = [this] {
@ -45,7 +45,7 @@ void IRCAppWindow::setup_client()
m_client.aid_update_window_list = [this] { m_client.aid_update_window_list = [this] {
m_window_list->model()->update(); m_window_list->model()->update();
}; };
m_client.on_nickname_changed = [this] (const String&) { m_client.on_nickname_changed = [this](const String&) {
update_title(); update_title();
}; };
@ -64,33 +64,33 @@ void IRCAppWindow::setup_client()
void IRCAppWindow::setup_actions() 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); GInputBox input_box("Enter channel name:", "Join channel", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty()) if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_join_action(input_box.text_value()); 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"); 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); GInputBox input_box("Enter nickname:", "IRC WHOIS lookup", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty()) if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_whois_action(input_box.text_value()); 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); GInputBox input_box("Enter nickname:", "Open IRC query with...", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty()) if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_open_query_action(input_box.text_value()); 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"); 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); GInputBox input_box("Enter nickname:", "Change nickname", this);
if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty()) if (input_box.exec() == GInputBox::ExecOK && !input_box.text_value().is_empty())
m_client.handle_change_nick_action(input_box.text_value()); m_client.handle_change_nick_action(input_box.text_value());
@ -101,7 +101,7 @@ void IRCAppWindow::setup_menus()
{ {
auto menubar = make<GMenuBar>(); auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("IRC Client"); 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"); dbgprintf("Terminal: Quit menu activated!\n");
GApplication::the().quit(0); GApplication::the().quit(0);
return; return;
@ -120,7 +120,7 @@ void IRCAppWindow::setup_menus()
menubar->add_menu(move(server_menu)); menubar->add_menu(move(server_menu));
auto help_menu = make<GMenu>("Help"); 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"); dbgprintf("FIXME: Implement Help/About\n");
})); }));
menubar->add_menu(move(help_menu)); 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_activates_on_selection(true);
m_window_list->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill); m_window_list->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
m_window_list->set_preferred_size({ 100, 0 }); 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()); auto& window = m_client.window_at(index.row());
m_container->set_active_widget(&window); m_container->set_active_widget(&window);
window.clear_unread_count(); window.clear_unread_count();

View file

@ -1,6 +1,6 @@
#include "IRCChannel.h" #include "IRCChannel.h"
#include "IRCClient.h"
#include "IRCChannelMemberListModel.h" #include "IRCChannelMemberListModel.h"
#include "IRCClient.h"
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
@ -37,7 +37,7 @@ void IRCChannel::add_member(const String& name, char prefix)
void IRCChannel::remove_member(const String& name) 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) 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 String IRCChannelMemberListModel::column_name(int column) const
{ {
switch (column) { switch (column) {
case Column::Name: return "Name"; case Column::Name:
return "Name";
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -33,7 +34,8 @@ String IRCChannelMemberListModel::column_name(int column) const
GModel::ColumnMetadata IRCChannelMemberListModel::column_metadata(int column) const GModel::ColumnMetadata IRCChannelMemberListModel::column_metadata(int column) const
{ {
switch (column) { switch (column) {
case Column::Name: return { 70, TextAlignment::CenterLeft }; case Column::Name:
return { 70, TextAlignment::CenterLeft };
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -42,10 +44,11 @@ GVariant IRCChannelMemberListModel::data(const GModelIndex& index, Role role) co
{ {
if (role == Role::Display) { if (role == Role::Display) {
switch (index.column()) { 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() void IRCChannelMemberListModel::update()

View file

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

View file

@ -1,16 +1,16 @@
#include "IRCClient.h" #include "IRCClient.h"
#include "IRCChannel.h" #include "IRCChannel.h"
#include "IRCQuery.h"
#include "IRCLogBuffer.h" #include "IRCLogBuffer.h"
#include "IRCQuery.h"
#include "IRCWindow.h" #include "IRCWindow.h"
#include "IRCWindowListModel.h" #include "IRCWindowListModel.h"
#include <LibCore/CNotifier.h> #include <LibCore/CNotifier.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <netinet/in.h>
#include <stdio.h> #include <stdio.h>
#include <sys/socket.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#define IRC_DEBUG #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_hostname = hostname;
m_port = port; m_port = port;
@ -112,7 +112,8 @@ void IRCClient::process_line(ByteBuffer&& line)
InStartOfParameter, InStartOfParameter,
InParameter, InParameter,
InTrailingParameter, InTrailingParameter,
} state = Start; } state
= Start;
for (int i = 0; i < line.size(); ++i) { for (int i = 0; i < line.size(); ++i) {
char ch = line[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", printf("IRCClient::execute: prefix='%s', command='%s', arguments=%d\n",
msg.prefix.characters(), msg.prefix.characters(),
msg.command.characters(), msg.command.characters(),
msg.arguments.size() msg.arguments.size());
);
int i = 0; int i = 0;
for (auto& arg : msg.arguments) { for (auto& arg : msg.arguments) {
@ -231,16 +231,26 @@ void IRCClient::handle(const Message& msg)
if (is_numeric) { if (is_numeric) {
switch (numeric) { switch (numeric) {
case RPL_WHOISCHANNELS: return handle_rpl_whoischannels(msg); case RPL_WHOISCHANNELS:
case RPL_ENDOFWHOIS: return handle_rpl_endofwhois(msg); return handle_rpl_whoischannels(msg);
case RPL_WHOISOPERATOR: return handle_rpl_whoisoperator(msg); case RPL_ENDOFWHOIS:
case RPL_WHOISSERVER: return handle_rpl_whoisserver(msg); return handle_rpl_endofwhois(msg);
case RPL_WHOISUSER: return handle_rpl_whoisuser(msg); case RPL_WHOISOPERATOR:
case RPL_WHOISIDLE: return handle_rpl_whoisidle(msg); return handle_rpl_whoisoperator(msg);
case RPL_TOPICWHOTIME: return handle_rpl_topicwhotime(msg); case RPL_WHOISSERVER:
case RPL_TOPIC: return handle_rpl_topic(msg); return handle_rpl_whoisserver(msg);
case RPL_NAMREPLY: return handle_rpl_namreply(msg); case RPL_WHOISUSER:
case RPL_ENDOFNAMES: return handle_rpl_endofnames(msg); 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; return;
auto& channel_name = msg.arguments[1]; auto& channel_name = msg.arguments[1];
auto& topic = msg.arguments[2]; 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. // 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(), nick.characters(),
username.characters(), username.characters(),
host.characters(), host.characters(),
realname.characters() realname.characters()));
));
} }
void IRCClient::handle_rpl_whoisidle(const Message& msg) 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_mday,
tm->tm_hour, tm->tm_hour,
tm->tm_min, 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); 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 "IRCLogBufferModel.h"
#include "IRCLogBuffer.h" #include "IRCLogBuffer.h"
#include <SharedGraphics/Font.h>
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include <SharedGraphics/Font.h>
IRCLogBufferModel::IRCLogBufferModel(Retained<IRCLogBuffer>&& log_buffer) IRCLogBufferModel::IRCLogBufferModel(Retained<IRCLogBuffer>&& log_buffer)
: m_log_buffer(move(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 String IRCLogBufferModel::column_name(int column) const
{ {
switch (column) { switch (column) {
case Column::Timestamp: return "Time"; case Column::Timestamp:
case Column::Name: return "Name"; return "Time";
case Column::Text: return "Text"; case Column::Name:
return "Name";
case Column::Text:
return "Text";
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -36,9 +39,12 @@ String IRCLogBufferModel::column_name(int column) const
GModel::ColumnMetadata IRCLogBufferModel::column_metadata(int column) const GModel::ColumnMetadata IRCLogBufferModel::column_metadata(int column) const
{ {
switch (column) { switch (column) {
case Column::Timestamp: return { 60, TextAlignment::CenterLeft }; case Column::Timestamp:
case Column::Name: return { 70, TextAlignment::CenterRight, &Font::default_bold_font() }; return { 60, TextAlignment::CenterLeft };
case Column::Text: return { 800, TextAlignment::CenterLeft }; case Column::Name:
return { 70, TextAlignment::CenterRight, &Font::default_bold_font() };
case Column::Text:
return { 800, TextAlignment::CenterLeft };
} }
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
@ -56,7 +62,8 @@ GVariant IRCLogBufferModel::data(const GModelIndex& index, Role role) const
if (entry.sender.is_empty()) if (entry.sender.is_empty())
return String::empty(); return String::empty();
return String::format("<%c%s>", entry.prefix ? entry.prefix : ' ', entry.sender.characters()); 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) { if (role == Role::ForegroundColor) {
@ -65,7 +72,7 @@ GVariant IRCLogBufferModel::data(const GModelIndex& index, Role role) const
if (index.column() == Column::Text) if (index.column() == Column::Text)
return m_log_buffer->at(index.row()).color; return m_log_buffer->at(index.row()).color;
} }
return { }; return {};
} }
void IRCLogBufferModel::update() void IRCLogBufferModel::update()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,8 +11,7 @@ class GraphWidget;
class ProcessModel final : public GModel { class ProcessModel final : public GModel {
public: public:
enum Column enum Column {
{
Icon = 0, Icon = 0,
Name, Name,
CPU, CPU,
@ -20,7 +19,7 @@ public:
Priority, Priority,
User, User,
PID, PID,
Linear, Virtual,
Physical, Physical,
Syscalls, Syscalls,
__Count __Count
@ -48,8 +47,8 @@ private:
String state; String state;
String user; String user;
String priority; String priority;
size_t linear; size_t virtual_size;
size_t physical; size_t physical_size;
unsigned syscalls; unsigned syscalls;
float cpu_percent; 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 "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) int main(int argc, char** argv)
{ {
@ -46,7 +46,7 @@ int main(int argc, char** argv)
cpu_graph->set_max(100); cpu_graph->set_max(100);
cpu_graph->set_text_color(Color::Green); cpu_graph->set_text_color(Color::Green);
cpu_graph->set_graph_color(Color::from_rgb(0x00bb00)); 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); return String::format("%d%%", value);
}; };
@ -58,7 +58,7 @@ int main(int argc, char** argv)
auto* memory_graph = new GraphWidget(memory_graph_group_box); auto* memory_graph = new GraphWidget(memory_graph_group_box);
memory_graph->set_text_color(Color::Cyan); memory_graph->set_text_color(Color::Cyan);
memory_graph->set_graph_color(Color::from_rgb(0x00bbbb)); 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); return String::format("%d / %d KB", value, max);
}; };
@ -78,19 +78,19 @@ int main(int argc, char** argv)
memory_stats_widget->refresh(); 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(); pid_t pid = process_table_view->selected_pid();
if (pid != -1) if (pid != -1)
kill(pid, SIGKILL); 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(); pid_t pid = process_table_view->selected_pid();
if (pid != -1) if (pid != -1)
kill(pid, SIGSTOP); 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(); pid_t pid = process_table_view->selected_pid();
if (pid != -1) if (pid != -1)
kill(pid, SIGCONT); kill(pid, SIGCONT);
@ -102,7 +102,7 @@ int main(int argc, char** argv)
auto menubar = make<GMenuBar>(); auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("Process Manager"); 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); GApplication::the().quit(0);
return; return;
})); }));
@ -115,25 +115,25 @@ int main(int argc, char** argv)
menubar->add_menu(move(process_menu)); menubar->add_menu(move(process_menu));
auto frequency_menu = make<GMenu>("Frequency"); 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); 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); 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); 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); 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); refresh_timer->restart(5000);
})); }));
menubar->add_menu(move(frequency_menu)); menubar->add_menu(move(frequency_menu));
auto help_menu = make<GMenu>("Help"); 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"); dbgprintf("FIXME: Implement Help/About\n");
})); }));
menubar->add_menu(move(help_menu)); menubar->add_menu(move(help_menu));

View file

@ -1,8 +1,8 @@
#include "TaskbarButton.h" #include "TaskbarButton.h"
#include <WindowServer/WSAPITypes.h>
#include <LibGUI/GAction.h> #include <LibGUI/GAction.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GEventLoop.h> #include <LibGUI/GEventLoop.h>
#include <LibGUI/GMenu.h>
#include <WindowServer/WSAPITypes.h>
static void set_window_minimized_state(const WindowIdentifier& identifier, bool minimized) static void set_window_minimized_state(const WindowIdentifier& identifier, bool minimized)
{ {
@ -34,13 +34,13 @@ GMenu& TaskbarButton::ensure_menu()
{ {
if (!m_menu) { if (!m_menu) {
m_menu = make<GMenu>(""); 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); 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); 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"); dbgprintf("FIXME: Close!\n");
})); }));
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -1,19 +1,19 @@
#include "Terminal.h" #include "Terminal.h"
#include "XtermColors.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/AKString.h>
#include <AK/StringBuilder.h>
#include <SharedGraphics/Font.h>
#include <LibGUI/GPainter.h>
#include <AK/StdLibExtras.h> #include <AK/StdLibExtras.h>
#include <LibGUI/GApplication.h> #include <AK/StringBuilder.h>
#include <LibGUI/GWindow.h>
#include <Kernel/KeyCode.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 <sys/ioctl.h>
#include <unistd.h>
//#define TERMINAL_DEBUG //#define TERMINAL_DEBUG
byte Terminal::Attribute::default_foreground_color = 7; 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()); 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", m_cursor_blink_timer.set_interval(m_config->read_num_entry("Text",
"CursorBlinkInterval", "CursorBlinkInterval",
500)); 500));
m_cursor_blink_timer.on_timeout = [this] { m_cursor_blink_timer.on_timeout = [this] {
m_cursor_blink_state = !m_cursor_blink_state; m_cursor_blink_state = !m_cursor_blink_state;
update_cursor(); update_cursor();
@ -43,7 +43,7 @@ Terminal::Terminal(int ptm_fd, RetainPtr<CConfigFile> config)
else else
set_font(Font::load_from_file(font_entry)); 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]; byte buffer[BUFSIZ];
ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer)); ssize_t nread = read(m_ptm_fd, buffer, sizeof(buffer));
if (nread < 0) { if (nread < 0) {
@ -65,37 +65,48 @@ Terminal::Terminal(int ptm_fd, RetainPtr<CConfigFile> config)
m_line_height = font().glyph_height() + m_line_spacing; m_line_height = font().glyph_height() + m_line_spacing;
set_size(m_config->read_num_entry("Window", "Width", 80), 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) Terminal::Line::Line(word length)
: length(columns)
{ {
characters = new byte[length]; set_length(length);
attributes = new Attribute[length];
memset(characters, ' ', length);
} }
Terminal::Line::~Line() Terminal::Line::~Line()
{ {
delete [] characters; delete[] characters;
delete [] attributes; 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) void Terminal::Line::clear(Attribute attribute)
{ {
if (dirty) { if (dirty) {
memset(characters, ' ', length); memset(characters, ' ', m_length);
for (word i = 0 ; i < length; ++i) for (word i = 0; i < m_length; ++i)
attributes[i] = attribute; attributes[i] = attribute;
return; return;
} }
for (unsigned i = 0 ; i < length; ++i) { for (unsigned i = 0; i < m_length; ++i) {
if (characters[i] != ' ') if (characters[i] != ' ')
dirty = true; dirty = true;
characters[i] = ' '; characters[i] = ' ';
} }
for (unsigned i = 0 ; i < length; ++i) { for (unsigned i = 0; i < m_length; ++i) {
if (attributes[i] != attribute) if (attributes[i] != attribute)
dirty = true; dirty = true;
attributes[i] = attribute; attributes[i] = attribute;
@ -104,10 +115,6 @@ void Terminal::Line::clear(Attribute attribute)
Terminal::~Terminal() Terminal::~Terminal()
{ {
for (int i = 0; i < m_rows; ++i)
delete m_lines[i];
delete [] m_lines;
free(m_horizontal_tabs);
} }
void Terminal::clear() void Terminal::clear()
@ -137,6 +144,35 @@ static inline Color lookup_color(unsigned color)
return Color::from_rgb(xterm_colors[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) void Terminal::escape$m(const ParamVector& params)
{ {
if (params.is_empty()) { if (params.is_empty()) {
@ -243,7 +279,7 @@ void Terminal::escape$t(const ParamVector& params)
{ {
if (params.size() < 1) if (params.size() < 1)
return; 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) void Terminal::escape$r(const ParamVector& params)
@ -254,7 +290,13 @@ void Terminal::escape$r(const ParamVector& params)
top = params[0]; top = params[0];
if (params.size() >= 2) if (params.size() >= 2)
bottom = params[1]; 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) 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); 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) void Terminal::escape$d(const ParamVector& params)
{ {
int new_row = 1; int new_row = 1;
@ -373,7 +424,10 @@ void Terminal::escape$K(const ParamVector& params)
} }
break; break;
case 2: case 2:
unimplemented_escape(); // Clear the complete line
for (int i = 0; i < m_columns; ++i) {
put_character_at(m_cursor_row, i, ' ');
}
break; break;
default: default:
unimplemented_escape(); unimplemented_escape();
@ -389,9 +443,8 @@ void Terminal::escape$J(const ParamVector& params)
switch (mode) { switch (mode) {
case 0: case 0:
// Clear from cursor to end of screen. // 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, ' '); put_character_at(m_cursor_row, i, ' ');
}
for (int row = m_cursor_row + 1; row < m_rows; ++row) { for (int row = m_cursor_row + 1; row < m_rows; ++row) {
for (int column = 0; column < m_columns; ++column) { for (int column = 0; column < m_columns; ++column) {
put_character_at(row, column, ' '); put_character_at(row, column, ' ');
@ -399,8 +452,14 @@ void Terminal::escape$J(const ParamVector& params)
} }
break; break;
case 1: case 1:
// FIXME: Clear from cursor to beginning of screen. /// Clear from cursor to beginning of screen
unimplemented_escape(); 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; break;
case 2: case 2:
clear(); 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) void Terminal::escape$M(const ParamVector& params)
{ {
int count = 1; int count = 1;
@ -426,12 +521,38 @@ void Terminal::escape$M(const ParamVector& params)
return; 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); count = min(count, max_count);
dbgprintf("Delete %d line(s) starting from %d\n", count, m_cursor_row); for (int c = count; c > 0; --c) {
// FIXME: Implement. m_lines.remove(m_cursor_row + m_scroll_region_top);
ASSERT_NOT_REACHED(); 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() void Terminal::execute_xterm_command()
@ -457,42 +578,122 @@ void Terminal::execute_xterm_command()
void Terminal::execute_escape_sequence(byte final) void Terminal::execute_escape_sequence(byte final)
{ {
bool question_param = false;
m_final = final; m_final = final;
auto paramparts = String::copy(m_parameters).split(';');
ParamVector params; 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) { for (auto& parampart : paramparts) {
bool ok; bool ok;
unsigned value = parampart.to_uint(ok); unsigned value = parampart.to_uint(ok);
if (!ok) { if (!ok) {
// FIXME: Should we do something else?
m_parameters.clear_with_capacity(); m_parameters.clear_with_capacity();
m_intermediates.clear_with_capacity(); m_intermediates.clear_with_capacity();
// FIXME: Should we do something else?
return; return;
} }
params.append(value); 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) { switch (final) {
case 'A': escape$A(params); break; case 'A':
case 'B': escape$B(params); break; escape$A(params);
case 'C': escape$C(params); break; break;
case 'D': escape$D(params); break; case 'B':
case 'H': escape$H(params); break; escape$B(params);
case 'J': escape$J(params); break; break;
case 'K': escape$K(params); break; case 'C':
case 'M': escape$M(params); break; escape$C(params);
case 'G': escape$G(params); break; break;
case 'X': escape$X(params); break; case 'D':
case 'd': escape$d(params); break; escape$D(params);
case 'm': escape$m(params); break; break;
case 's': escape$s(params); break; case 'H':
case 'u': escape$u(params); break; escape$H(params);
case 't': escape$t(params); break; break;
case 'r': escape$r(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: default:
dbgprintf("Terminal::execute_escape_sequence: Unhandled final '%c'\n", final); dbgprintf("Terminal::execute_escape_sequence: Unhandled final '%c'\n", final);
break; 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_parameters.clear_with_capacity();
m_intermediates.clear_with_capacity(); m_intermediates.clear_with_capacity();
} }
@ -500,7 +701,7 @@ void Terminal::execute_escape_sequence(byte final)
void Terminal::newline() void Terminal::newline()
{ {
word new_row = m_cursor_row; word new_row = m_cursor_row;
if (m_cursor_row == (rows() - 1)) { if (m_cursor_row == m_scroll_region_bottom) {
scroll_up(); scroll_up();
} else { } else {
++new_row; ++new_row;
@ -512,11 +713,17 @@ void Terminal::scroll_up()
{ {
// NOTE: We have to invalidate the cursor first. // NOTE: We have to invalidate the cursor first.
invalidate_cursor(); invalidate_cursor();
delete m_lines[0]; m_lines.remove(m_scroll_region_top);
for (word row = 1; row < rows(); ++row) m_lines.insert(m_scroll_region_bottom, make<Line>(m_columns));
m_lines[row - 1] = m_lines[row]; m_need_full_flush = true;
m_lines[m_rows - 1] = new Line(m_columns); }
++m_rows_to_scroll_backing_store;
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; m_need_full_flush = true;
} }
@ -531,7 +738,7 @@ void Terminal::set_cursor(unsigned a_row, unsigned a_column)
invalidate_cursor(); invalidate_cursor();
m_cursor_row = row; m_cursor_row = row;
m_cursor_column = column; m_cursor_column = column;
if (column != columns() - 1) if (column != columns() - 1u)
m_stomp = false; m_stomp = false;
invalidate_cursor(); invalidate_cursor();
} }
@ -541,11 +748,11 @@ void Terminal::put_character_at(unsigned row, unsigned column, byte ch)
ASSERT(row < rows()); ASSERT(row < rows());
ASSERT(column < columns()); ASSERT(column < columns());
auto& line = this->line(row); auto& line = this->line(row);
if ((line.characters[column] == ch) && (line.attributes[column] == m_current_attribute))
return;
line.characters[column] = ch; line.characters[column] = ch;
line.attributes[column] = m_current_attribute; line.attributes[column] = m_current_attribute;
line.dirty = true; line.dirty = true;
m_last_char = ch;
} }
void Terminal::on_char(byte ch) void Terminal::on_char(byte ch)
@ -628,7 +835,16 @@ void Terminal::on_char(byte ch)
} }
return; return;
case '\a': 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; return;
case '\t': { case '\t': {
for (unsigned i = m_cursor_column; i < columns(); ++i) { 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) if (columns == m_columns && rows == m_rows)
return; return;
if (m_lines) { #if defined(TERMINAL_DEBUG)
for (size_t i = 0; i < m_rows; ++i) dbgprintf("Terminal: RESIZE to: %d rows\n", rows);
delete m_lines[i]; #endif
delete m_lines;
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_columns = columns;
m_rows = rows; m_rows = rows;
m_scroll_region_top = 0;
m_scroll_region_bottom = rows - 1;
m_cursor_row = 0; m_cursor_row = 0;
m_cursor_column = 0; m_cursor_column = 0;
m_saved_cursor_row = 0; m_saved_cursor_row = 0;
m_saved_cursor_column = 0; m_saved_cursor_column = 0;
if (m_horizontal_tabs) m_horizontal_tabs.resize(columns);
free(m_horizontal_tabs);
m_horizontal_tabs = static_cast<byte*>(malloc(columns));
for (unsigned i = 0; i < columns; ++i) for (unsigned i = 0; i < columns; ++i)
m_horizontal_tabs[i] = (i % 8) == 0; m_horizontal_tabs[i] = (i % 8) == 0;
// Rightmost column is always last tab on line. // Rightmost column is always last tab on line.
m_horizontal_tabs[columns - 1] = 1; 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_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; 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_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
set_preferred_size({ m_pixel_width, m_pixel_height }); set_preferred_size({ m_pixel_width, m_pixel_height });
m_rows_to_scroll_backing_store = 0;
m_needs_background_fill = true; m_needs_background_fill = true;
force_repaint(); force_repaint();
@ -760,11 +980,11 @@ Rect Terminal::row_rect(word row)
bool Terminal::Line::has_only_one_background_color() const bool Terminal::Line::has_only_one_background_color() const
{ {
if (!length) if (!m_length)
return true; return true;
// FIXME: Cache this result? // FIXME: Cache this result?
auto color = attributes[0].background_color; 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) if (attributes[i].background_color != color)
return false; return false;
} }
@ -816,6 +1036,11 @@ void Terminal::keydown_event(GKeyEvent& event)
case KeyCode::Key_End: case KeyCode::Key_End:
write(m_ptm_fd, "\033[F", 3); write(m_ptm_fd, "\033[F", 3);
break; 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: default:
write(m_ptm_fd, &ch, 1); write(m_ptm_fd, &ch, 1);
break; break;
@ -823,45 +1048,28 @@ void Terminal::keydown_event(GKeyEvent& event)
} }
void Terminal::paint_event(GPaintEvent& event) void Terminal::paint_event(GPaintEvent& event)
{ {
GFrame::paint_event(event); GFrame::paint_event(event);
GPainter painter(*this); GPainter painter(*this);
if (m_needs_background_fill) { if (m_visual_beep_timer.is_active())
m_needs_background_fill = false; painter.fill_rect(frame_inner_rect(), Color::Red);
else
painter.fill_rect(frame_inner_rect(), Color(Color::Black).with_alpha(255 * m_opacity)); 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(); invalidate_cursor();
for (word row = 0; row < m_rows; ++row) { for (word row = 0; row < m_rows; ++row) {
auto& line = this->line(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(); 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)); 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) { 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; 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]; auto& attribute = line.attributes[column];
char ch = line.characters[column];
auto character_rect = glyph_rect(row, column); auto character_rect = glyph_rect(row, column);
if (!has_only_one_background_color || should_reverse_fill_for_cursor) { if (!has_only_one_background_color || should_reverse_fill_for_cursor) {
auto cell_rect = character_rect.inflated(0, m_line_spacing); 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); 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)); 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) void Terminal::set_window_title(const String& title)

View file

@ -26,6 +26,9 @@ public:
void apply_size_increments_to_window(GWindow&); void apply_size_increments_to_window(GWindow&);
void set_opacity(float); 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; } RetainPtr<CConfigFile> config() const { return m_config; }
@ -39,6 +42,7 @@ private:
virtual const char* class_name() const override { return "Terminal"; } virtual const char* class_name() const override { return "Terminal"; }
void scroll_up(); void scroll_up();
void scroll_down();
void newline(); void newline();
void set_cursor(unsigned row, unsigned column); void set_cursor(unsigned row, unsigned column);
void put_character_at(unsigned row, unsigned column, byte ch); void put_character_at(unsigned row, unsigned column, byte ch);
@ -57,14 +61,20 @@ private:
void escape$J(const ParamVector&); void escape$J(const ParamVector&);
void escape$K(const ParamVector&); void escape$K(const ParamVector&);
void escape$M(const ParamVector&); void escape$M(const ParamVector&);
void escape$P(const ParamVector&);
void escape$G(const ParamVector&); void escape$G(const ParamVector&);
void escape$X(const ParamVector&); void escape$X(const ParamVector&);
void escape$b(const ParamVector&);
void escape$d(const ParamVector&); void escape$d(const ParamVector&);
void escape$m(const ParamVector&); void escape$m(const ParamVector&);
void escape$s(const ParamVector&); void escape$s(const ParamVector&);
void escape$u(const ParamVector&); void escape$u(const ParamVector&);
void escape$t(const ParamVector&); void escape$t(const ParamVector&);
void escape$r(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(); void clear();
@ -118,10 +128,11 @@ private:
~Line(); ~Line();
void clear(Attribute); void clear(Attribute);
bool has_only_one_background_color() const; bool has_only_one_background_color() const;
void set_length(word);
byte* characters { nullptr }; byte* characters { nullptr };
Attribute* attributes { nullptr }; Attribute* attributes { nullptr };
bool dirty { false }; bool dirty { false };
word length { 0 }; word m_length { 0 };
}; };
Line& line(size_t index) Line& line(size_t index)
{ {
@ -129,7 +140,10 @@ private:
return *m_lines[index]; 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_columns { 0 };
word m_rows { 0 }; word m_rows { 0 };
@ -140,13 +154,14 @@ private:
byte m_saved_cursor_column { 0 }; byte m_saved_cursor_column { 0 };
bool m_stomp { false }; bool m_stomp { false };
bool m_should_beep { false };
Attribute m_current_attribute; Attribute m_current_attribute;
void execute_escape_sequence(byte final); void execute_escape_sequence(byte final);
void execute_xterm_command(); void execute_xterm_command();
enum EscapeState enum EscapeState {
{
Normal, Normal,
ExpectBracket, ExpectBracket,
ExpectParameter, ExpectParameter,
@ -162,13 +177,12 @@ private:
Vector<byte> m_intermediates; Vector<byte> m_intermediates;
Vector<byte> m_xterm_param1; Vector<byte> m_xterm_param1;
Vector<byte> m_xterm_param2; Vector<byte> m_xterm_param2;
Vector<bool> m_horizontal_tabs;
byte m_final { 0 }; byte m_final { 0 };
byte* m_horizontal_tabs { nullptr };
bool m_belling { false }; bool m_belling { false };
int m_pixel_width { 0 }; int m_pixel_width { 0 };
int m_pixel_height { 0 }; int m_pixel_height { 0 };
int m_rows_to_scroll_backing_store { 0 };
int m_inset { 2 }; int m_inset { 2 };
int m_line_spacing { 4 }; int m_line_spacing { 4 };
@ -190,5 +204,8 @@ private:
int m_glyph_width { 0 }; int m_glyph_width { 0 };
CTimer m_cursor_blink_timer; CTimer m_cursor_blink_timer;
CTimer m_visual_beep_timer;
RetainPtr<CConfigFile> m_config; 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 "Terminal.h"
#include <Kernel/KeyCode.h> #include <Kernel/KeyCode.h>
#include <LibCore/CUserInfo.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GApplication.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/GWidget.h>
#include <LibGUI/GWindow.h> #include <LibGUI/GWindow.h>
#include <LibGUI/GMenuBar.h> #include <assert.h>
#include <LibGUI/GAction.h> #include <errno.h>
#include <LibGUI/GFontDatabase.h> #include <fcntl.h>
#include <LibGUI/GSlider.h> #include <pwd.h>
#include <LibCore/CUserInfo.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) 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. // NOTE: It's okay if this fails.
(void) ioctl(0, TIOCNOTTY); (void)ioctl(0, TIOCNOTTY);
close(0); close(0);
close(1); 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) int main(int argc, char** argv)
{ {
GApplication app(argc, argv); GApplication app(argc, argv);
@ -106,23 +157,9 @@ int main(int argc, char** argv)
terminal.apply_size_increments_to_window(*window); terminal.apply_size_increments_to_window(*window);
window->show(); window->show();
window->set_icon_path("/res/icons/16x16/app-terminal.png"); 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; WeakPtr<GWindow> settings_window;
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);
auto new_opacity = config->read_num_entry("Window", "Opacity", 255); auto new_opacity = config->read_num_entry("Window", "Opacity", 255);
terminal.set_opacity((float)new_opacity / 255.0); terminal.set_opacity((float)new_opacity / 255.0);
@ -130,10 +167,14 @@ int main(int argc, char** argv)
auto menubar = make<GMenuBar>(); auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("Terminal"); auto app_menu = make<GMenu>("Terminal");
app_menu->add_action(GAction::create("Adjust opacity...", [opacity_adjustment_window] (const GAction&) { app_menu->add_action(GAction::create("Settings...",
opacity_adjustment_window->show(); [&settings_window, &terminal, &config](const GAction&) {
})); if (!settings_window)
app_menu->add_action(GAction::create("Quit", { Mod_Alt, Key_F4 }, [] (const GAction&) { 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"); dbgprintf("Terminal: Quit menu activated!\n");
GApplication::the().quit(0); GApplication::the().quit(0);
return; return;
@ -141,8 +182,8 @@ int main(int argc, char** argv)
menubar->add_menu(move(app_menu)); menubar->add_menu(move(app_menu));
auto font_menu = make<GMenu>("Font"); auto font_menu = make<GMenu>("Font");
GFontDatabase::the().for_each_fixed_width_font([&] (const String& font_name) { GFontDatabase::the().for_each_fixed_width_font([&](const StringView& font_name) {
font_menu->add_action(GAction::create(font_name, [&terminal, &config] (const GAction& action) { font_menu->add_action(GAction::create(font_name, [&terminal, &config](const GAction& action) {
terminal.set_font(GFontDatabase::the().get_by_name(action.text())); terminal.set_font(GFontDatabase::the().get_by_name(action.text()));
auto metadata = GFontDatabase::the().get_metadata_by_name(action.text()); auto metadata = GFontDatabase::the().get_metadata_by_name(action.text());
config->write_entry("Text", "Font", metadata.path); config->write_entry("Text", "Font", metadata.path);
@ -153,7 +194,7 @@ int main(int argc, char** argv)
menubar->add_menu(move(font_menu)); menubar->add_menu(move(font_menu));
auto help_menu = make<GMenu>("Help"); 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"); dbgprintf("FIXME: Implement Help/About\n");
})); }));
menubar->add_menu(move(help_menu)); menubar->add_menu(move(help_menu));

View file

@ -1,21 +1,21 @@
#include <LibGUI/GWindow.h> #include <AK/StringBuilder.h>
#include <LibGUI/GWidget.h> #include <LibCore/CFile.h>
#include <LibGUI/GBoxLayout.h> #include <LibGUI/GAction.h>
#include <LibGUI/GApplication.h> #include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h>
#include <LibGUI/GFilePicker.h> #include <LibGUI/GFilePicker.h>
#include <LibGUI/GFontDatabase.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GMessageBox.h> #include <LibGUI/GMessageBox.h>
#include <LibGUI/GStatusBar.h> #include <LibGUI/GStatusBar.h>
#include <LibGUI/GToolBar.h>
#include <LibGUI/GMenuBar.h>
#include <LibGUI/GTextEditor.h> #include <LibGUI/GTextEditor.h>
#include <LibGUI/GAction.h> #include <LibGUI/GToolBar.h>
#include <LibGUI/GFontDatabase.h> #include <LibGUI/GWidget.h>
#include <LibCore/CFile.h> #include <LibGUI/GWindow.h>
#include <AK/StringBuilder.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void open_sesame(GWindow& window, GTextEditor& editor, const String& path) 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); 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"); 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()); dbgprintf("Writing document to '%s'\n", path.characters());
text_editor->write_to_file(path); text_editor->write_to_file(path);
}); });
auto menubar = make<GMenuBar>(); auto menubar = make<GMenuBar>();
auto app_menu = make<GMenu>("Text Editor"); 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); GApplication::the().quit(0);
return; return;
})); }));
@ -99,8 +99,8 @@ int main(int argc, char** argv)
menubar->add_menu(move(edit_menu)); menubar->add_menu(move(edit_menu));
auto font_menu = make<GMenu>("Font"); auto font_menu = make<GMenu>("Font");
GFontDatabase::the().for_each_fixed_width_font([&] (const String& font_name) { GFontDatabase::the().for_each_fixed_width_font([&](const StringView& font_name) {
font_menu->add_action(GAction::create(font_name, [text_editor] (const GAction& action) { 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->set_font(GFontDatabase::the().get_by_name(action.text()));
text_editor->update(); text_editor->update();
})); }));
@ -108,7 +108,7 @@ int main(int argc, char** argv)
menubar->add_menu(move(font_menu)); menubar->add_menu(move(font_menu));
auto help_menu = make<GMenu>("Help"); 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"); dbgprintf("FIXME: Implement Help/About\n");
})); }));
menubar->add_menu(move(help_menu)); menubar->add_menu(move(help_menu));

View file

@ -1,3 +1,2 @@
[DNS] [DNS]
IPAddress=8.8.8.8 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] [Window]
Opacity=255 Opacity=255
AudibleBeep=0

View file

@ -1,5 +1,5 @@
[Screen] [Screen]
Width=1080 Width=1024
Height=768 Height=768
[Cursor] [Cursor]
@ -36,4 +36,4 @@ MenuSelectionColor=132,53,26
DoubleClickSpeed=250 DoubleClickSpeed=250
[Background] [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/GApplication.h>
#include <LibGUI/GWindow.h>
#include <LibGUI/GWidget.h>
#include <LibGUI/GLabel.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GBoxLayout.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) int main(int argc, char** argv)
{ {
@ -27,7 +27,7 @@ int main(int argc, char** argv)
button->set_text("Good-bye"); button->set_text("Good-bye");
button->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); button->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
button->set_preferred_size({ 0, 20 }); button->set_preferred_size({ 0, 20 });
button->on_click = [&] (GButton&) { button->on_click = [&](GButton&) {
app.quit(); app.quit();
}; };

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