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>
|
2022-02-15 14:28:43 -03:00
|
|
|
* Copyright (c) 2022, the SerenityOS developers.
|
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>
|
2021-11-24 22:54:16 -03:00
|
|
|
#include <LibCore/System.h>
|
2020-04-18 07:23:56 -03:00
|
|
|
#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>
|
2021-11-24 22:54:16 -03:00
|
|
|
#include <LibMain/Main.h>
|
2020-04-18 07:23:56 -03:00
|
|
|
#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
|
|
|
|
2022-04-01 14:58:27 -03:00
|
|
|
int const WIDTH = 200;
|
|
|
|
|
int const HEIGHT = 200;
|
2020-04-18 07:23:56 -03:00
|
|
|
|
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:
|
2022-02-15 14:28:43 -03:00
|
|
|
virtual ~Cube() override = default;
|
2020-04-18 07:23:56 -03:00
|
|
|
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-11-06 15:30:59 -03:00
|
|
|
m_bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRx8888, { WIDTH, HEIGHT }).release_value_but_fixme_should_propagate_errors();
|
2020-04-18 07:23:56 -03:00
|
|
|
|
|
|
|
|
m_accumulated_time = 0;
|
|
|
|
|
m_cycles = 0;
|
|
|
|
|
m_phase = 0;
|
|
|
|
|
|
|
|
|
|
stop_timer();
|
|
|
|
|
start_timer(20);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2022-04-01 14:58:27 -03:00
|
|
|
int const indices[] {
|
2020-04-18 07:23:56 -03:00
|
|
|
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
|
|
|
|
2022-04-01 14:58:27 -03:00
|
|
|
auto to_point = [](FloatVector3 const& 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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-24 22:54:16 -03:00
|
|
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
2020-04-18 07:23:56 -03:00
|
|
|
{
|
2021-11-24 22:54:16 -03:00
|
|
|
auto app = TRY(GUI::Application::try_create(arguments));
|
2020-04-18 07:23:56 -03:00
|
|
|
|
2021-11-27 19:26:34 -03:00
|
|
|
TRY(Core::System::pledge("stdio recvfd sendfd rpath"));
|
2021-11-24 22:54:16 -03:00
|
|
|
TRY(Core::System::unveil("/res", "r"));
|
|
|
|
|
TRY(Core::System::unveil(nullptr, nullptr));
|
2020-11-01 16:37:48 -03:00
|
|
|
|
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');
|
2021-11-24 22:54:16 -03:00
|
|
|
parser.parse(arguments);
|
2021-03-01 01:59:21 -03:00
|
|
|
|
2021-11-24 22:54:16 -03:00
|
|
|
auto window = TRY(GUI::Window::try_create());
|
2020-04-18 07:23:56 -03:00
|
|
|
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
|
|
|
|
2021-11-24 22:54:16 -03:00
|
|
|
auto cube = TRY(window->try_set_main_widget<Cube>());
|
2020-04-18 07:23:56 -03:00
|
|
|
|
2021-11-24 22:54:16 -03:00
|
|
|
auto time = TRY(cube->try_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-04-18 07:23:56 -03:00
|
|
|
|
2022-07-11 14:32:29 -03:00
|
|
|
auto app_icon = GUI::Icon::default_icon("app-cube"sv);
|
2020-10-31 18:23:42 -03:00
|
|
|
window->set_icon(app_icon.bitmap_for_size(16));
|
2020-04-18 07:23:56 -03:00
|
|
|
|
2021-11-24 22:54:16 -03:00
|
|
|
auto file_menu = TRY(window->try_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-11-24 22:54:16 -03:00
|
|
|
cube->set_show_window_frame(action.is_checked());
|
2021-02-21 03:14:15 -03:00
|
|
|
});
|
2021-03-01 01:59:21 -03:00
|
|
|
|
2021-11-24 22:54:16 -03:00
|
|
|
cube->set_show_window_frame(!flag_hide_window_frame);
|
|
|
|
|
show_window_frame_action->set_checked(cube->show_window_frame());
|
|
|
|
|
TRY(file_menu->try_add_action(move(show_window_frame_action)));
|
|
|
|
|
TRY(file_menu->try_add_separator());
|
|
|
|
|
TRY(file_menu->try_add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })));
|
|
|
|
|
auto help_menu = TRY(window->try_add_menu("&Help"));
|
|
|
|
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Cube Demo", app_icon, window)));
|
2021-02-21 03:14:15 -03:00
|
|
|
|
2021-11-24 22:54:16 -03:00
|
|
|
cube->on_context_menu_request = [&](auto& event) {
|
|
|
|
|
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
|
|
|
}
|