From 174ac5d34842ea4f20d01b340945e265df8e4afc Mon Sep 17 00:00:00 2001 From: Sergey Bugaev Date: Sun, 24 May 2020 21:08:26 +0300 Subject: Demos: Add Eyes demo This is a neat clone of xeyes. It demoes the new ellipse filling functionality, as well as the existing global mouse cursor tracking functionality :^) --- Demos/CMakeLists.txt | 1 + Demos/Eyes/CMakeLists.txt | 7 +++ Demos/Eyes/EyesWidget.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++ Demos/Eyes/EyesWidget.h | 51 ++++++++++++++++++ Demos/Eyes/main.cpp | 62 ++++++++++++++++++++++ 5 files changed, 250 insertions(+) create mode 100644 Demos/Eyes/CMakeLists.txt create mode 100644 Demos/Eyes/EyesWidget.cpp create mode 100644 Demos/Eyes/EyesWidget.h create mode 100644 Demos/Eyes/main.cpp (limited to 'Demos') diff --git a/Demos/CMakeLists.txt b/Demos/CMakeLists.txt index da387e0a8e..65bc6fe9f6 100644 --- a/Demos/CMakeLists.txt +++ b/Demos/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(Cube) add_subdirectory(DynamicLink) +add_subdirectory(Eyes) add_subdirectory(Fire) add_subdirectory(HelloWorld) add_subdirectory(LibGfxDemo) diff --git a/Demos/Eyes/CMakeLists.txt b/Demos/Eyes/CMakeLists.txt new file mode 100644 index 0000000000..355a874673 --- /dev/null +++ b/Demos/Eyes/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES + main.cpp + EyesWidget.cpp +) + +serenity_bin(Eyes) +target_link_libraries(Eyes LibGUI LibGfx) diff --git a/Demos/Eyes/EyesWidget.cpp b/Demos/Eyes/EyesWidget.cpp new file mode 100644 index 0000000000..e7a229c33c --- /dev/null +++ b/Demos/Eyes/EyesWidget.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "EyesWidget.h" +#include +#include +#include +#include +#include + +EyesWidget::~EyesWidget() +{ +} + +void EyesWidget::track_cursor_globally() +{ + ASSERT(window()); + auto window_id = window()->window_id(); + ASSERT(window_id >= 0); + + set_global_cursor_tracking(true); + GUI::WindowServerConnection::the().send_sync(window_id, true); +} + +void EyesWidget::mousemove_event(GUI::MouseEvent& event) +{ + m_mouse_position = event.position(); + update(); +} + +void EyesWidget::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + + painter.clear_rect(event.rect(), Gfx::Color()); + + for (int i = 0; i < m_num_eyes; i++) + render_eyeball(i, painter); +} + +void EyesWidget::render_eyeball(int index, GUI::Painter& painter) const +{ + auto eye_width = width() / m_num_eyes; + Gfx::Rect bounds { index * eye_width, 0, eye_width, height() }; + auto thickness = max((width() + height()) / 15, 1); + + bounds.shrink(width() / 25, 0); + painter.fill_ellipse(bounds, palette().base_text()); + bounds.shrink(thickness, thickness); + painter.fill_ellipse(bounds, palette().base()); + + Gfx::Point pupil_center = this->pupil_center(bounds); + Gfx::Size pupil_size { + bounds.width() / 5, + bounds.height() / 5 + }; + Gfx::Rect pupil { + pupil_center.x() - pupil_size.width() / 2, + pupil_center.y() - pupil_size.height() / 2, + pupil_size.width(), + pupil_size.height() + }; + + painter.fill_ellipse(pupil, palette().base_text()); +} + +Gfx::Point EyesWidget::pupil_center(Gfx::Rect& eyeball_bounds) const +{ + auto mouse_vector = m_mouse_position - eyeball_bounds.center(); + double dx = mouse_vector.x(); + double dy = mouse_vector.y(); + double mouse_distance = sqrt(dx * dx + dy * dy); + + if (mouse_distance == 0.0) + return eyeball_bounds.center(); + + double width_squared = eyeball_bounds.width() * eyeball_bounds.width(); + double height_squared = eyeball_bounds.height() * eyeball_bounds.height(); + + double max_distance_along_this_direction; + + if (dx != 0 && abs(dx) >= abs(dy)) { + double slope = dy / dx; + double slope_squared = slope * slope; + max_distance_along_this_direction = 0.25 * sqrt( + (slope_squared + 1) / + (1 / width_squared + slope_squared / height_squared) + ); + } else if (dy != 0 && abs(dy) >= abs(dx)) { + double slope = dx / dy; + double slope_squared = slope * slope; + max_distance_along_this_direction = 0.25 * sqrt( + (slope_squared + 1) / + (slope_squared / width_squared + 1 / height_squared) + ); + } else { + ASSERT_NOT_REACHED(); + } + + double scale = min(1.0, max_distance_along_this_direction / mouse_distance); + + return { + eyeball_bounds.center().x() + int(dx * scale), + eyeball_bounds.center().y() + int(dy * scale) + }; +} diff --git a/Demos/Eyes/EyesWidget.h b/Demos/Eyes/EyesWidget.h new file mode 100644 index 0000000000..bec31cd03f --- /dev/null +++ b/Demos/Eyes/EyesWidget.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +class EyesWidget final : public GUI::Widget { + C_OBJECT(EyesWidget) + +public: + virtual ~EyesWidget(); + void track_cursor_globally(); + +private: + EyesWidget(int num_eyes) + : m_num_eyes(num_eyes) + { + } + + virtual void mousemove_event(GUI::MouseEvent&) override; + virtual void paint_event(GUI::PaintEvent&) override; + + void render_eyeball(int index, GUI::Painter&) const; + Gfx::Point pupil_center(Gfx::Rect& eyeball_bounds) const; + + Gfx::Point m_mouse_position; + int m_num_eyes { -1 }; +}; diff --git a/Demos/Eyes/main.cpp b/Demos/Eyes/main.cpp new file mode 100644 index 0000000000..0ac003c982 --- /dev/null +++ b/Demos/Eyes/main.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Sergey Bugaev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "EyesWidget.h" +#include +#include +#include + +int main(int argc, char* argv[]) +{ + int num_eyes = 2; + + Core::ArgsParser args_parser; + args_parser.add_option(num_eyes, "Number of eyes", "num-eyes", 'n', "number"); + args_parser.parse(argc, argv); + + if (pledge("stdio shared_buffer accept rpath unix cpath wpath fattr thread", nullptr) < 0) { + perror("pledge"); + return 1; + } + + GUI::Application app(argc, argv); + + if (pledge("stdio shared_buffer accept rpath cpath wpath thread", nullptr) < 0) { + perror("pledge"); + return 1; + } + + auto window = GUI::Window::construct(); + window->set_title("Eyes"); + window->set_rect(350, 270, 150, 100); + window->set_has_alpha_channel(true); + + auto& eyes = window->set_main_widget(num_eyes); + window->show(); + eyes.track_cursor_globally(); + + return app.exec(); +} -- cgit v1.2.3