/* * Copyright (c) 2021, Richard Gráčik * Copyright (c) 2022, kleines Filmröllchen * * SPDX-License-Identifier: BSD-2-Clause */ #include "CatDog.h" #include #include #include #include #include ErrorOr> 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 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); }