summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Bugaev <bugaevc@serenityos.org>2020-05-24 21:08:26 +0300
committerAndreas Kling <kling@serenityos.org>2020-05-24 23:30:12 +0200
commit174ac5d34842ea4f20d01b340945e265df8e4afc (patch)
tree563730ce3c01e0334703cf16dc7b01d024a61c48
parent8520d3a4252e2b366598210ebc322ff3561d5381 (diff)
downloadserenity-174ac5d34842ea4f20d01b340945e265df8e4afc.zip
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 :^)
-rw-r--r--Base/res/apps/Eyes.af4
-rw-r--r--Demos/CMakeLists.txt1
-rw-r--r--Demos/Eyes/CMakeLists.txt7
-rw-r--r--Demos/Eyes/EyesWidget.cpp129
-rw-r--r--Demos/Eyes/EyesWidget.h51
-rw-r--r--Demos/Eyes/main.cpp62
6 files changed, 254 insertions, 0 deletions
diff --git a/Base/res/apps/Eyes.af b/Base/res/apps/Eyes.af
new file mode 100644
index 0000000000..b86a1c1180
--- /dev/null
+++ b/Base/res/apps/Eyes.af
@@ -0,0 +1,4 @@
+[App]
+Name=Eyes
+Executable=/bin/Eyes
+Category=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 <bugaevc@serenityos.org>
+ * 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 <LibGUI/Painter.h>
+#include <LibGUI/Window.h>
+#include <LibGUI/WindowServerConnection.h>
+#include <LibGfx/Palette.h>
+#include <math.h>
+
+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<Messages::WindowServer::SetGlobalCursorTracking>(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 <bugaevc@serenityos.org>
+ * 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 <LibGUI/Widget.h>
+#include <LibGfx/Point.h>
+
+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 <bugaevc@serenityos.org>
+ * 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 <LibCore/ArgsParser.h>
+#include <LibGUI/Application.h>
+#include <LibGUI/Window.h>
+
+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<EyesWidget>(num_eyes);
+ window->show();
+ eyes.track_cursor_globally();
+
+ return app.exec();
+}