mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 10:22:05 -05:00
6da1d5eb57
Now you can put your CatDog to sleep while you're at work :)
202 lines
6.9 KiB
C++
202 lines
6.9 KiB
C++
/*
|
|
* Copyright (c) 2021, Richard Gráčik <r.gracik@gmail.com>
|
|
* Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "CatDog.h"
|
|
#include <LibCore/File.h>
|
|
#include <LibCore/ProcessStatisticsReader.h>
|
|
#include <LibGUI/Menu.h>
|
|
#include <LibGUI/Painter.h>
|
|
#include <LibGUI/Window.h>
|
|
|
|
ErrorOr<NonnullRefPtr<CatDog>> CatDog::create()
|
|
{
|
|
struct ImageSource {
|
|
State state;
|
|
StringView path;
|
|
};
|
|
|
|
// NOTE: The order of the elements is important. Matching is done in best-match order.
|
|
// So items with the more bits should be placed before items with less bits to
|
|
// ensure correct matching order. This also means that "Frame2" has to be first.
|
|
static constexpr Array<ImageSource, 24> const image_sources = {
|
|
ImageSource { State::Up | State::Right | State::Frame2, "/res/graphics/catdog/nerun2.png"sv },
|
|
{ State::Up | State::Right, "/res/graphics/catdog/nerun1.png"sv },
|
|
{ State::Up | State::Left | State::Frame2, "/res/graphics/catdog/nwrun2.png"sv },
|
|
{ State::Up | State::Left, "/res/graphics/catdog/nwrun1.png"sv },
|
|
{ State::Down | State::Right | State::Frame2, "/res/graphics/catdog/serun2.png"sv },
|
|
{ State::Down | State::Right, "/res/graphics/catdog/serun1.png"sv },
|
|
{ State::Down | State::Left | State::Frame2, "/res/graphics/catdog/swrun2.png"sv },
|
|
{ State::Down | State::Left, "/res/graphics/catdog/swrun1.png"sv },
|
|
{ State::Up | State::Frame2, "/res/graphics/catdog/nrun2.png"sv },
|
|
{ State::Up, "/res/graphics/catdog/nrun1.png"sv },
|
|
{ State::Down | State::Frame2, "/res/graphics/catdog/srun2.png"sv },
|
|
{ State::Down, "/res/graphics/catdog/srun1.png"sv },
|
|
{ State::Left | State::Frame2, "/res/graphics/catdog/wrun2.png"sv },
|
|
{ State::Left, "/res/graphics/catdog/wrun1.png"sv },
|
|
{ State::Right | State::Frame2, "/res/graphics/catdog/erun2.png"sv },
|
|
{ State::Right, "/res/graphics/catdog/erun1.png"sv },
|
|
{ State::Sleeping | State::Frame2, "/res/graphics/catdog/sleep2.png"sv },
|
|
{ State::Sleeping, "/res/graphics/catdog/sleep1.png"sv },
|
|
{ State::Idle | State::Artist, "/res/graphics/catdog/artist.png"sv },
|
|
{ State::Idle | State::Inspector, "/res/graphics/catdog/inspector.png"sv },
|
|
{ State::Idle, "/res/graphics/catdog/still.png"sv },
|
|
{ State::Alert | State::Artist, "/res/graphics/catdog/artist.png"sv },
|
|
{ State::Alert | State::Inspector, "/res/graphics/catdog/inspector.png"sv },
|
|
{ State::Alert, "/res/graphics/catdog/alert.png"sv }
|
|
};
|
|
|
|
auto catdog = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) CatDog));
|
|
for (auto const& image_source : image_sources)
|
|
TRY(catdog->m_images.try_append({ image_source.state, *TRY(Gfx::Bitmap::load_from_file(image_source.path)) }));
|
|
|
|
return catdog;
|
|
}
|
|
|
|
CatDog::CatDog()
|
|
: m_proc_all(MUST(Core::File::open("/sys/kernel/processes"sv, Core::File::OpenMode::Read)))
|
|
{
|
|
m_idle_sleep_timer.start();
|
|
}
|
|
|
|
void CatDog::set_roaming(bool roaming)
|
|
{
|
|
m_state = (roaming ? State::Idle : State::Alert) | special_application_states();
|
|
update();
|
|
}
|
|
|
|
void CatDog::set_sleeping(bool sleeping)
|
|
{
|
|
m_state = (sleeping ? State::Sleeping : State::Idle) | special_application_states();
|
|
update();
|
|
}
|
|
|
|
CatDog::State CatDog::special_application_states() const
|
|
{
|
|
auto maybe_proc_info = Core::ProcessStatisticsReader::get_all(*m_proc_all);
|
|
if (maybe_proc_info.is_error())
|
|
return State::GenericCatDog;
|
|
|
|
auto proc_info = maybe_proc_info.release_value();
|
|
auto maybe_paint_program = proc_info.processes.first_matching([](auto& process) {
|
|
return process.name.equals_ignoring_ascii_case("pixelpaint"sv) || process.name.equals_ignoring_ascii_case("fonteditor"sv);
|
|
});
|
|
if (maybe_paint_program.has_value())
|
|
return State::Artist;
|
|
|
|
auto maybe_inspector_program = proc_info.processes.first_matching([](auto& process) {
|
|
return process.name.equals_ignoring_ascii_case("systemmonitor"sv) || process.name.equals_ignoring_ascii_case("profiler"sv);
|
|
});
|
|
if (maybe_inspector_program.has_value())
|
|
return State::Inspector;
|
|
|
|
return State::GenericCatDog;
|
|
}
|
|
|
|
bool CatDog::is_artist() const
|
|
{
|
|
return has_flag(special_application_states(), State::Artist);
|
|
}
|
|
|
|
bool CatDog::is_inspector() const
|
|
{
|
|
return has_flag(special_application_states(), State::Inspector);
|
|
}
|
|
|
|
bool CatDog::is_sleeping() const
|
|
{
|
|
return has_flag(m_state, State::Sleeping);
|
|
}
|
|
|
|
void CatDog::timer_event(Core::TimerEvent&)
|
|
{
|
|
using namespace AK::TimeLiterals;
|
|
|
|
if (has_flag(m_state, State::Alert))
|
|
return;
|
|
|
|
if (has_flag(m_state, State::Sleeping))
|
|
return;
|
|
|
|
m_state = special_application_states();
|
|
|
|
auto const size = window()->size();
|
|
Gfx::IntPoint move;
|
|
|
|
if (m_mouse_offset.x() < 0) {
|
|
m_state |= State::Left;
|
|
move.set_x(max(m_mouse_offset.x(), -size.width() / 2));
|
|
} else if (m_mouse_offset.x() > size.width()) {
|
|
m_state |= State::Right;
|
|
move.set_x(min(m_mouse_offset.x(), size.width() / 2));
|
|
}
|
|
|
|
if (m_mouse_offset.y() < 0) {
|
|
m_state |= State::Up;
|
|
move.set_y(max(m_mouse_offset.y(), -size.height() / 2));
|
|
} else if (m_mouse_offset.y() > size.height()) {
|
|
m_state |= State::Down;
|
|
move.set_y(min(m_mouse_offset.y(), size.height() / 2));
|
|
}
|
|
|
|
if (has_any_flag(m_state, State::Directions)) {
|
|
m_idle_sleep_timer.start();
|
|
} else {
|
|
if (m_idle_sleep_timer.elapsed_time() > 5_sec)
|
|
m_state |= State::Sleeping;
|
|
else
|
|
m_state |= State::Idle;
|
|
}
|
|
|
|
window()->move_to(window()->position() + move);
|
|
m_mouse_offset -= move;
|
|
|
|
m_frame = m_frame == State::Frame1 ? State::Frame2 : State::Frame1;
|
|
m_state |= m_frame;
|
|
|
|
update();
|
|
}
|
|
|
|
Gfx::Bitmap& CatDog::bitmap_for_state() const
|
|
{
|
|
auto const iter = m_images.find_if([&](auto const& image) { return (m_state & image.state) == image.state; });
|
|
return iter != m_images.end() ? *iter->bitmap : *m_images[m_images.size() - 1].bitmap;
|
|
}
|
|
|
|
void CatDog::paint_event(GUI::PaintEvent& event)
|
|
{
|
|
auto const& bmp = bitmap_for_state();
|
|
GUI::Painter painter(*this);
|
|
painter.clear_rect(event.rect(), Gfx::Color());
|
|
painter.blit(Gfx::IntPoint(0, 0), bmp, bmp.rect());
|
|
}
|
|
|
|
void CatDog::track_mouse_move(Gfx::IntPoint point)
|
|
{
|
|
if (has_flag(m_state, State::Alert))
|
|
return;
|
|
|
|
if (has_flag(m_state, State::Sleeping))
|
|
return;
|
|
|
|
Gfx::IntPoint relative_offset = point - window()->position();
|
|
if (m_mouse_offset != relative_offset) {
|
|
m_mouse_offset = relative_offset;
|
|
m_idle_sleep_timer.start();
|
|
}
|
|
}
|
|
|
|
void CatDog::mousedown_event(GUI::MouseEvent& event)
|
|
{
|
|
if (event.button() == GUI::MouseButton::Primary && on_click)
|
|
on_click();
|
|
}
|
|
|
|
void CatDog::context_menu_event(GUI::ContextMenuEvent& event)
|
|
{
|
|
if (on_context_menu_request)
|
|
on_context_menu_request(event);
|
|
}
|