2020-04-18 07:23:56 -03:00
|
|
|
/*
|
2021-05-29 07:38:28 -03:00
|
|
|
* Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org>
|
2020-04-18 07:23:56 -03:00
|
|
|
*
|
2021-04-22 05:24:48 -03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-04-18 07:23:56 -03:00
|
|
|
*/
|
|
|
|
|
|
2021-03-01 01:59:21 -03:00
|
|
|
#include <LibCore/ArgsParser.h>
|
2020-04-18 07:23:56 -03:00
|
|
|
#include <LibCore/ElapsedTimer.h>
|
|
|
|
|
#include <LibGUI/Application.h>
|
2020-10-31 18:23:42 -03:00
|
|
|
#include <LibGUI/Icon.h>
|
2020-04-18 07:23:56 -03:00
|
|
|
#include <LibGUI/Label.h>
|
2021-02-21 03:14:15 -03:00
|
|
|
#include <LibGUI/Menu.h>
|
2021-04-13 11:18:20 -03:00
|
|
|
#include <LibGUI/Menubar.h>
|
2020-04-18 07:23:56 -03:00
|
|
|
#include <LibGUI/Painter.h>
|
|
|
|
|
#include <LibGUI/Widget.h>
|
|
|
|
|
#include <LibGUI/Window.h>
|
|
|
|
|
#include <LibGfx/Bitmap.h>
|
|
|
|
|
#include <LibGfx/Matrix4x4.h>
|
|
|
|
|
#include <LibGfx/Vector3.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2021-03-12 13:29:37 -03:00
|
|
|
#include <unistd.h>
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
const int WIDTH = 200;
|
|
|
|
|
const int HEIGHT = 200;
|
|
|
|
|
|
2021-03-01 01:59:21 -03:00
|
|
|
static bool flag_hide_window_frame = false;
|
|
|
|
|
|
2020-04-18 07:23:56 -03:00
|
|
|
class Cube final : public GUI::Widget {
|
|
|
|
|
C_OBJECT(Cube)
|
|
|
|
|
public:
|
|
|
|
|
virtual ~Cube() override;
|
|
|
|
|
void set_stat_label(RefPtr<GUI::Label> l) { m_stats = l; };
|
2021-02-21 03:14:15 -03:00
|
|
|
void set_show_window_frame(bool);
|
|
|
|
|
bool show_window_frame() const { return m_show_window_frame; }
|
|
|
|
|
|
|
|
|
|
Function<void(GUI::ContextMenuEvent&)> on_context_menu_request;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual void context_menu_event(GUI::ContextMenuEvent& event) override
|
|
|
|
|
{
|
|
|
|
|
if (on_context_menu_request)
|
|
|
|
|
on_context_menu_request(event);
|
|
|
|
|
}
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Cube();
|
2021-02-21 03:14:15 -03:00
|
|
|
|
2020-04-18 07:23:56 -03:00
|
|
|
RefPtr<Gfx::Bitmap> m_bitmap;
|
|
|
|
|
RefPtr<GUI::Label> m_stats;
|
|
|
|
|
|
|
|
|
|
virtual void paint_event(GUI::PaintEvent&) override;
|
|
|
|
|
virtual void timer_event(Core::TimerEvent&) override;
|
|
|
|
|
|
|
|
|
|
int m_accumulated_time;
|
|
|
|
|
int m_cycles;
|
|
|
|
|
int m_phase;
|
2021-02-21 03:14:15 -03:00
|
|
|
bool m_show_window_frame { true };
|
2020-04-18 07:23:56 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Cube::Cube()
|
|
|
|
|
{
|
2021-07-21 13:02:15 -03:00
|
|
|
m_bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRx8888, { WIDTH, HEIGHT });
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
m_accumulated_time = 0;
|
|
|
|
|
m_cycles = 0;
|
|
|
|
|
m_phase = 0;
|
|
|
|
|
|
|
|
|
|
stop_timer();
|
|
|
|
|
start_timer(20);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Cube::~Cube()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Cube::paint_event(GUI::PaintEvent& event)
|
|
|
|
|
{
|
|
|
|
|
GUI::Painter painter(*this);
|
|
|
|
|
painter.add_clip_rect(event.rect());
|
2021-05-09 05:11:30 -03:00
|
|
|
painter.draw_scaled_bitmap(rect(), *m_bitmap, m_bitmap->rect());
|
2020-04-18 07:23:56 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Cube::timer_event(Core::TimerEvent&)
|
|
|
|
|
{
|
2021-09-12 12:52:45 -03:00
|
|
|
auto timer = Core::ElapsedTimer::start_new();
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
const FloatVector3 vertices[8] {
|
|
|
|
|
{ -1, -1, -1 },
|
|
|
|
|
{ -1, 1, -1 },
|
|
|
|
|
{ 1, 1, -1 },
|
|
|
|
|
{ 1, -1, -1 },
|
|
|
|
|
{ -1, -1, 1 },
|
|
|
|
|
{ -1, 1, 1 },
|
|
|
|
|
{ 1, 1, 1 },
|
|
|
|
|
{ 1, -1, 1 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define QUAD(a, b, c, d) a, b, c, c, d, a
|
|
|
|
|
|
|
|
|
|
const int indices[] {
|
|
|
|
|
QUAD(0, 1, 2, 3),
|
|
|
|
|
QUAD(7, 6, 5, 4),
|
|
|
|
|
QUAD(4, 5, 1, 0),
|
|
|
|
|
QUAD(3, 2, 6, 7),
|
|
|
|
|
QUAD(1, 5, 6, 2),
|
|
|
|
|
QUAD(0, 3, 7, 4),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Color colors[] {
|
|
|
|
|
Color::Red,
|
|
|
|
|
Color::Red,
|
|
|
|
|
Color::Green,
|
|
|
|
|
Color::Green,
|
|
|
|
|
Color::Blue,
|
|
|
|
|
Color::Blue,
|
|
|
|
|
Color::Magenta,
|
|
|
|
|
Color::Magenta,
|
|
|
|
|
Color::White,
|
|
|
|
|
Color::White,
|
|
|
|
|
Color::Yellow,
|
|
|
|
|
Color::Yellow,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FloatVector3 transformed_vertices[8];
|
|
|
|
|
|
|
|
|
|
static float angle = 0;
|
|
|
|
|
angle += 0.02f;
|
|
|
|
|
|
2021-05-13 16:33:17 -03:00
|
|
|
auto matrix = Gfx::translation_matrix(FloatVector3(0, 0, 1.5f))
|
|
|
|
|
* Gfx::rotation_matrix(FloatVector3(1, 0, 0), angle * 1.17356641f)
|
|
|
|
|
* Gfx::rotation_matrix(FloatVector3(0, 1, 0), angle * 0.90533273f)
|
|
|
|
|
* Gfx::rotation_matrix(FloatVector3(0, 0, 1), angle);
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
for (int i = 0; i < 8; i++) {
|
2021-05-13 16:33:17 -03:00
|
|
|
transformed_vertices[i] = transform_point(matrix, vertices[i]);
|
2020-04-18 07:23:56 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GUI::Painter painter(*m_bitmap);
|
2021-02-21 03:14:15 -03:00
|
|
|
if (m_show_window_frame)
|
|
|
|
|
painter.fill_rect_with_gradient(Gfx::Orientation::Vertical, m_bitmap->rect(), Gfx::Color::White, Gfx::Color::Blue);
|
|
|
|
|
else
|
|
|
|
|
painter.clear_rect(m_bitmap->rect(), Gfx::Color::Transparent);
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
auto to_point = [](const FloatVector3& v) {
|
2020-06-10 05:57:59 -03:00
|
|
|
return Gfx::IntPoint(v.x(), v.y());
|
2020-04-18 07:23:56 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof(indices) / sizeof(indices[0]) / 3; i++) {
|
|
|
|
|
auto a = transformed_vertices[indices[i * 3]];
|
|
|
|
|
auto b = transformed_vertices[indices[i * 3 + 1]];
|
|
|
|
|
auto c = transformed_vertices[indices[i * 3 + 2]];
|
|
|
|
|
auto normal = (b - a).cross(c - a);
|
|
|
|
|
normal.normalize();
|
|
|
|
|
|
|
|
|
|
// Perspective projection
|
2021-04-15 04:36:14 -03:00
|
|
|
a.set_x(WIDTH / 2 + a.x() / (1 + a.z() * 0.35f) * WIDTH / 3);
|
|
|
|
|
a.set_y(HEIGHT / 2 - a.y() / (1 + a.z() * 0.35f) * WIDTH / 3);
|
|
|
|
|
b.set_x(WIDTH / 2 + b.x() / (1 + b.z() * 0.35f) * WIDTH / 3);
|
|
|
|
|
b.set_y(HEIGHT / 2 - b.y() / (1 + b.z() * 0.35f) * WIDTH / 3);
|
|
|
|
|
c.set_x(WIDTH / 2 + c.x() / (1 + c.z() * 0.35f) * WIDTH / 3);
|
|
|
|
|
c.set_y(HEIGHT / 2 - c.y() / (1 + c.z() * 0.35f) * WIDTH / 3);
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
float winding = (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
|
|
|
|
|
if (winding < 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
float shade = 0.5f + normal.y() * 0.5f;
|
|
|
|
|
auto color = colors[i];
|
|
|
|
|
color.set_red(color.red() * shade);
|
|
|
|
|
color.set_green(color.green() * shade);
|
|
|
|
|
color.set_blue(color.blue() * shade);
|
|
|
|
|
|
|
|
|
|
painter.draw_triangle(to_point(a), to_point(b), to_point(c), color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((m_cycles % 50) == 0) {
|
2020-10-07 07:21:32 -03:00
|
|
|
dbgln("{} total cycles. finished 50 in {} ms, avg {} ms", m_cycles, m_accumulated_time, m_accumulated_time / 50);
|
|
|
|
|
m_stats->set_text(String::formatted("{} ms", m_accumulated_time / 50));
|
2020-04-18 07:23:56 -03:00
|
|
|
m_accumulated_time = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update();
|
|
|
|
|
|
|
|
|
|
m_accumulated_time += timer.elapsed();
|
|
|
|
|
m_cycles++;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-21 03:14:15 -03:00
|
|
|
void Cube::set_show_window_frame(bool show)
|
|
|
|
|
{
|
|
|
|
|
if (show == m_show_window_frame)
|
|
|
|
|
return;
|
|
|
|
|
m_show_window_frame = show;
|
|
|
|
|
m_stats->set_visible(m_show_window_frame);
|
|
|
|
|
auto& w = *window();
|
|
|
|
|
w.set_frameless(!m_show_window_frame);
|
|
|
|
|
w.set_has_alpha_channel(!m_show_window_frame);
|
|
|
|
|
w.set_alpha_hit_threshold(m_show_window_frame ? 0 : 1);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-18 07:23:56 -03:00
|
|
|
int main(int argc, char** argv)
|
|
|
|
|
{
|
2020-07-04 09:05:19 -03:00
|
|
|
auto app = GUI::Application::construct(argc, argv);
|
2020-04-18 07:23:56 -03:00
|
|
|
|
2021-01-16 13:42:31 -03:00
|
|
|
if (pledge("stdio recvfd sendfd rpath", nullptr) < 0) {
|
2020-11-01 16:37:48 -03:00
|
|
|
perror("pledge");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unveil("/res", "r") < 0) {
|
|
|
|
|
perror("unveil");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unveil(nullptr, nullptr) < 0) {
|
|
|
|
|
perror("unveil");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-01 01:59:21 -03:00
|
|
|
Core::ArgsParser parser;
|
|
|
|
|
parser.set_general_help("Create a window with a spinning cube.");
|
|
|
|
|
parser.add_option(flag_hide_window_frame, "Hide window frame", "hide-window", 'h');
|
|
|
|
|
parser.parse(argc, argv);
|
|
|
|
|
|
2020-04-18 07:23:56 -03:00
|
|
|
auto window = GUI::Window::construct();
|
|
|
|
|
window->set_double_buffering_enabled(true);
|
|
|
|
|
window->set_title("Cube");
|
|
|
|
|
window->set_resizable(false);
|
Misc: Use automatic window positioning in more applications
This is a follow up to #2936 / d3e3b4ae56aa79d9bde12ca1f143dcf116f89a4c.
Affected programs:
- Applications: Browser (Download, View source, Inspect DOM tree, JS
console), Terminal (Settings)
- Demos: Cube, Eyes, Fire, HelloWorld, LibGfxDemo, WebView,
WidgetGallery
- DevTools: HackStudio, Inspector, Profiler
- Games: 2048, Minesweeper, Snake, Solitaire
- Userland: test-web
A few have been left out where manual positioning is done on purpose,
e.g. ClipboardManager (to be close to the menu bar) or VisualBuilder (to
preserve alignment of the multiple application windows).
2020-08-15 12:24:05 -03:00
|
|
|
window->resize(WIDTH, HEIGHT);
|
2021-02-21 03:14:15 -03:00
|
|
|
window->set_has_alpha_channel(true);
|
|
|
|
|
window->set_alpha_hit_threshold(1);
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
auto& cube = window->set_main_widget<Cube>();
|
|
|
|
|
|
|
|
|
|
auto& time = cube.add<GUI::Label>();
|
|
|
|
|
time.set_relative_rect({ 0, 4, 40, 10 });
|
|
|
|
|
time.move_by({ window->width() - time.width(), 0 });
|
|
|
|
|
cube.set_stat_label(time);
|
|
|
|
|
|
2020-10-31 18:23:42 -03:00
|
|
|
auto app_icon = GUI::Icon::default_icon("app-cube");
|
|
|
|
|
window->set_icon(app_icon.bitmap_for_size(16));
|
2020-04-18 07:23:56 -03:00
|
|
|
|
2021-07-21 16:21:03 -03:00
|
|
|
auto& file_menu = window->add_menu("&File");
|
2021-05-12 14:09:42 -03:00
|
|
|
auto show_window_frame_action = GUI::Action::create_checkable("Show Window &Frame", [&](auto& action) {
|
2021-02-21 03:14:15 -03:00
|
|
|
cube.set_show_window_frame(action.is_checked());
|
|
|
|
|
});
|
2021-03-01 01:59:21 -03:00
|
|
|
|
|
|
|
|
cube.set_show_window_frame(!flag_hide_window_frame);
|
2021-02-21 03:14:15 -03:00
|
|
|
show_window_frame_action->set_checked(cube.show_window_frame());
|
2021-05-01 05:45:39 -03:00
|
|
|
file_menu.add_action(move(show_window_frame_action));
|
|
|
|
|
file_menu.add_separator();
|
|
|
|
|
file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); }));
|
2021-07-21 16:21:03 -03:00
|
|
|
auto& help_menu = window->add_menu("&Help");
|
2021-02-21 03:14:15 -03:00
|
|
|
help_menu.add_action(GUI::CommonActions::make_about_action("Cube Demo", app_icon, window));
|
|
|
|
|
|
|
|
|
|
cube.on_context_menu_request = [&](auto& event) {
|
2021-05-01 05:45:39 -03:00
|
|
|
file_menu.popup(event.screen_position());
|
2021-02-21 03:14:15 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window->show();
|
|
|
|
|
|
2020-07-04 09:05:19 -03:00
|
|
|
return app->exec();
|
2020-04-18 07:23:56 -03:00
|
|
|
}
|