mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:48:11 +00:00

Moved all images into a Vector instead of storing every animation frame in its own member variable. This greatly simplifies the bitmap selection logic and removes redundant if() checks. Also fixes minor state bugs. For example, CatDog woudld go to sleep immediately after actively moving for > 5 seconds. Also fixes arbitrary hardcoded values for mouse offset movement tresholds as well as inconsistent movement increments. This allows clicking anywhere on the CatDog window without moving CatDog. In addition to removing many member variables, the API interface is also cleaned up a bit to expose less CatDog internals. Nobody likes exposed CatDog internals ;). Variables and function are also renamed where necessary to (hopefully) improve readability.
181 lines
6.4 KiB
C++
181 lines
6.4 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/ProcessStatisticsReader.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/icons/catdog/nerun2.png"sv },
|
|
{ State::Up | State::Right, "/res/icons/catdog/nerun1.png"sv },
|
|
{ State::Up | State::Left | State::Frame2, "/res/icons/catdog/nwrun2.png"sv },
|
|
{ State::Up | State::Left, "/res/icons/catdog/nwrun1.png"sv },
|
|
{ State::Down | State::Right | State::Frame2, "/res/icons/catdog/serun2.png"sv },
|
|
{ State::Down | State::Right, "/res/icons/catdog/serun1.png"sv },
|
|
{ State::Down | State::Left | State::Frame2, "/res/icons/catdog/swrun2.png"sv },
|
|
{ State::Down | State::Left, "/res/icons/catdog/swrun1.png"sv },
|
|
{ State::Up | State::Frame2, "/res/icons/catdog/nrun2.png"sv },
|
|
{ State::Up, "/res/icons/catdog/nrun1.png"sv },
|
|
{ State::Down | State::Frame2, "/res/icons/catdog/srun2.png"sv },
|
|
{ State::Down, "/res/icons/catdog/srun1.png"sv },
|
|
{ State::Left | State::Frame2, "/res/icons/catdog/wrun2.png"sv },
|
|
{ State::Left, "/res/icons/catdog/wrun1.png"sv },
|
|
{ State::Right | State::Frame2, "/res/icons/catdog/erun2.png"sv },
|
|
{ State::Right, "/res/icons/catdog/erun1.png"sv },
|
|
{ State::Sleeping | State::Frame2, "/res/icons/catdog/sleep2.png"sv },
|
|
{ State::Sleeping, "/res/icons/catdog/sleep1.png"sv },
|
|
{ State::Idle | State::Artist, "/res/icons/catdog/artist.png"sv },
|
|
{ State::Idle | State::Inspector, "/res/icons/catdog/inspector.png"sv },
|
|
{ State::Idle, "/res/icons/catdog/still.png"sv },
|
|
{ State::Alert | State::Artist, "/res/icons/catdog/artist.png"sv },
|
|
{ State::Alert | State::Inspector, "/res/icons/catdog/inspector.png"sv },
|
|
{ State::Alert, "/res/icons/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::try_load_from_file(image_source.path)) }));
|
|
|
|
return catdog;
|
|
}
|
|
|
|
CatDog::CatDog()
|
|
: m_proc_all(MUST(Core::Stream::File::open("/sys/kernel/processes"sv, Core::Stream::OpenMode::Read)))
|
|
{
|
|
m_idle_sleep_timer.start();
|
|
}
|
|
|
|
void CatDog::set_roaming(bool roaming)
|
|
{
|
|
m_state = (roaming ? State::Idle : State::Alert) | 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_case("pixelpaint"sv) || process.name.equals_ignoring_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_case("inspector"sv) || process.name.equals_ignoring_case("systemmonitor"sv) || process.name.equals_ignoring_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);
|
|
}
|
|
|
|
void CatDog::timer_event(Core::TimerEvent&)
|
|
{
|
|
if (has_flag(m_state, State::Alert))
|
|
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() > 5'000)
|
|
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;
|
|
|
|
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);
|
|
}
|