#pragma once #include "log.h" #include "shader.h" #include "shape.h" #include "texture.h" #include "font.h" #include "rtt.h" #include "image.h" #include "mp4enc.h" #include "node_message_box.h" #include "node_settings.h" #include "node_popup_menu.h" #include "node_panel_brush.h" #include "node_panel_layer.h" #include "node_panel_color.h" #include "node_panel_stroke.h" #include "node_scroll.h" #include "node_canvas.h" #include "node_dialog_layer_rename.h" #include "node_progress_bar.h" #include "node_panel_quick.h" #include "node_input_box.h" #include "node_panel_animation.h" #include "layout.h" #include "app_core/document_session.h" #include "app_core/app_thread.h" namespace pp::platform { class PlatformServices; struct PlatformStoragePaths; } class NodePanelGrid; #if defined(__OBJC__) && defined(__IOS__) @class GameViewController; @class AppDelegate; #endif #if defined(__OBJC__) && defined(__OSX__) @class View; @class AppOSX; #endif #ifdef __ANDROID__ struct android_app; struct engine; #endif #if __LINUX__ || __WEB__ struct GLFWwindow; #endif struct VRController { enum class kButton : uint8_t { Pad, Trigger, Menu, Grip, A, COUNT, }; enum class kAction { Press, Release, }; std::array m_buttons; std::array m_analog_buttons; glm::mat4 m_mat; bool m_valid = false; glm::vec3 get_pos() const { return m_mat[3]; } glm::vec3 get_pos_n() const { return glm::normalize(xyz(m_mat[3])); } virtual float get_trigger_value() const { return 1.f; } }; struct AppTask : public std::packaged_task { size_t task_id; #ifdef _DEBUG std::string name; #endif template AppTask(F f) : std::packaged_task(f) { task_id = typeid(f).hash_code(); #ifdef _DEBUG name = typeid(f).name(); #endif } }; class App { public: static App* I; std::string data_path{ "." }; std::string work_path{ "." }; std::string rec_path{ "." }; std::string tmp_path{ "." }; std::thread rec_thread; bool rec_running = false; int rec_count = 0; std::mutex rec_mutex; std::condition_variable rec_cv; std::deque> rec_frames; RTT uirtt; Sampler sampler; Sampler sampler_stencil; Sampler sampler_linear; Plane m_face_plane; Sphere sphere; LayoutManager layout; LayoutManager layout_designer; NodeMessageBox* msgbox; NodeSettings* settings; NodeBorder* sidebar = nullptr; std::shared_ptr layers; std::shared_ptr color; std::shared_ptr stroke; std::shared_ptr grid; std::shared_ptr presets; std::shared_ptr animation; NodePanelQuick* quick; std::map quick_mode_state; Node* floatings_container; std::shared_ptr floating_color; std::shared_ptr floating_picker; std::shared_ptr floating_presets; std::shared_ptr floating_layers; NodeCanvas* canvas; const uint16_t main_id = const_hash("main"); const std::array res_map{ 512, 1024, 1536, 2048, 4096, 8192 }; const std::array res_map_str{ "2K", "4K", "6K", "8K", "16K", "32K" }; std::string doc_name = "no-name"; std::string doc_path; std::string doc_dir; std::string doc_filename; bool has_stylus = false; bool has_vr = false; bool vr_controllers_enabled = true; float off_x = 0; float off_y = 0; float width; float height; bool keys[256]{false}; bool redraw = true; bool animate = false; bool ui_visible = true; bool ui_rtl = false; bool vr_active = false; bool vr_only = false; VRController vr_controllers[2]; glm::mat4 vr_head; float vr_pressure = 1.f; glm::mat4 vr_rot{ 0 }; glm::mat4 vr_uirot{ 0 }; glm::vec2 cursor{ 0, 0 }; glm::vec2 gesture_p0; glm::vec2 gesture_p1; float display_density = 1.f; float zoom = 1.f; int idle_ms = 100; pp::platform::PlatformServices* platform_services_ = nullptr; #if defined(__IOS__) && defined(__OBJC__) GameViewController* ios_view; AppDelegate* ios_app; #elif defined(__OSX__) && defined(__OBJC__) View* osx_view; AppOSX* osx_app; #elif __LINUX__ || __WEB__ GLFWwindow* glfw_window; #endif #ifdef __ANDROID__ struct android_app* and_app; struct engine* and_engine; #endif std::string clipboard_get_text(); bool clipboard_set_text(const std::string& s); void pick_image(std::function callback); void pick_file(std::vector types, std::function callback); void pick_file_save(const std::string& type, const std::string& default_name, std::function writer, std::function callback); void pick_file_save(std::vector types, std::function callback); [[nodiscard]] bool supports_working_directory_picker() const; [[nodiscard]] std::string format_working_directory_path(std::string_view path) const; [[nodiscard]] bool uses_prepared_file_writes() const; [[nodiscard]] bool uses_work_directory_document_export_collections() const; [[nodiscard]] bool disables_network_tls_verification() const; [[nodiscard]] bool uses_ppbr_export_data_directory_override() const; [[nodiscard]] bool platform_supports_sonarpen() const; void start_platform_sonarpen(); [[nodiscard]] int default_canvas_resolution() const; [[nodiscard]] bool draws_canvas_tip_for_input(kEventSource source, kEventType type) const; [[nodiscard]] float adjust_canvas_input_pressure(float pressure) const; void pick_dir(std::function callback); void display_file(std::string path); void share_file(std::string path); void request_app_close(); [[nodiscard]] bool start_platform_vr_mode(); void stop_platform_vr_mode(); void attach_ui_thread(); void detach_ui_thread(); void acquire_render_context(); void release_render_context(); void present_render_context(); void bind_default_render_target(); void bind_main_render_target(); void apply_render_platform_hints(); void install_render_debug_callback(); void begin_render_capture_frame(); void end_render_capture_frame(); [[nodiscard]] bool platform_deletes_recorded_files_on_clear(); void clear_platform_recorded_files(std::string path); void publish_exported_image(std::string path); void flush_platform_storage(); [[nodiscard]] std::vector document_browse_roots() const; void save_platform_ui_state(); [[nodiscard]] bool platform_enables_live_asset_reloading(); void update_platform_frame(float delta_time_seconds); void report_rendered_frames(int frames); void save_prepared_file( std::string path, std::string suggested_name, std::function callback); void set_platform_services(pp::platform::PlatformServices* services) noexcept; [[nodiscard]] pp::platform::PlatformServices* platform_services() const noexcept; [[nodiscard]] pp::platform::PlatformStoragePaths prepare_storage_paths(); void showKeyboard(); void hideKeyboard(); void initLog(); void init(); void initShaders(); void initAssets(); void initLayout(); void open_document(std::string path); void create(); bool request_close(); void terminate(); void clear(); void tick(float dt); void draw(float dt); void update(float dt); bool update_ui_observer(Node* n); bool vr_start(); void vr_stop(); void vr_update(float dt); void vr_draw(const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose); void vr_analog(const VRController& c, VRController::kButton b, VRController::kAction a, glm::vec2 force); void vr_digital(const VRController& c, VRController::kButton b, VRController::kAction a, glm::vec2 axis); void vr_draw_ui(); void async_start(); void async_redraw(); void async_swap(); void async_end(); void resize(float w, float h); void show_cursor(); void hide_cursor(); bool mouse_down(int button, float x, float y, float pressure, kEventSource source, bool eraser); bool mouse_move(float x, float y, float pressure, kEventSource source, bool eraser); bool mouse_up(int button, float x, float y, kEventSource source, bool eraser); bool mouse_scroll(float x, float y, float delta); bool mouse_cancel(int button); bool gesture_start(const glm::vec2& p0, const glm::vec2& p1); bool gesture_move(const glm::vec2& p0, const glm::vec2& p1); bool gesture_end(); bool touch_tap(const glm::vec2& pos, int fingers, int tap_count); bool key_down(kKey key); bool key_up(kKey key); bool key_char(char key); void toggle_ui(); void set_stylus(); void renderdoc_frame_start(); void renderdoc_frame_end(); void rec_clear(); void rec_loop(); void rec_start(); void rec_stop(); void rec_export(std::string path); void init_toolbar_main(); void init_toolbar_draw(); void init_sidebar(); void init_menu_file(); void init_menu_edit(); void init_menu_layer(); void init_menu_tools(); void init_menu_about(); void dialog_usermanual(); void dialog_changelog(); void dialog_about(); void save_document(pp::app::DocumentSaveIntent intent); void continue_document_workflow_after_optional_save(std::function action); void dialog_newdoc(); void dialog_save(); void dialog_save_ver(); void dialog_open(); void dialog_browse(); void dialog_export(std::string ext); void dialog_export_layers(); void dialog_export_anim_frames(); void dialog_export_depth(); void dialog_export_cube_faces(); void dialog_layer_rename(); void dialog_resize(); void dialog_preset_download(); void dialog_ppbr_export(); void dialog_export_mp4(); void dialog_timelapse_export(); void dialog_whatsnew(bool force_show); void dialog_shortcuts(); void cloud_upload(); void cloud_upload_all(); void cloud_browse(); void upload(std::string filename, std::string name = "", std::function progress = nullptr); void download(std::string url, std::string dest_filepath, std::function progress = nullptr); bool check_license(); std::shared_ptr show_progress(const std::string& title, int total = 0); std::shared_ptr message_box(const std::string& title, const std::string& text, bool cancel_button = false); std::shared_ptr input_box(const std::string& title, const std::string& field_name, const std::string& ok_caption = "Ok"); void brush_update(bool update_color, bool update_brush); void title_update(); void update_memory_usage(size_t bytes); void update_rec_frames(); int res_from_index(int i); int res_to_index(int res); std::string res_to_string(int res); void crash_test(); void stacktrace(); void ui_save(); void ui_restore(); void set_ui_rtl(bool rtl); bool get_ui_rtl() const; void set_ui_scale(float scale); void cmd_convert(std::string pano_path, std::string out_path); ////////////////////////////////////////////////////////////////////////// // RENDER THREAD ////////////////////////////////////////////////////////////////////////// static std::deque render_tasklist; static std::mutex render_task_mutex; static std::condition_variable render_cv; static std::thread render_thread; static std::thread::id render_thread_id; static bool render_running; void render_thread_tick(); void render_thread_main(); void render_thread_start(); void render_thread_stop(); bool is_render_thread() { return std::this_thread::get_id() == render_thread_id; } // don't capture a reference to this ptr as the object may be destroyed // by the time the task is executed template std::future render_task_async(T task, bool unique = false) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_render_thread(), unique, 0U, render_running, false, false); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(render_task_mutex); const auto queue_dispatch = pp::app::plan_app_task_dispatch( false, unique, render_tasklist.size(), render_running, false, false); // remove any previously queued task from the same lambda if (queue_dispatch.remove_matching_unique_task) render_tasklist.erase(std::remove_if(render_tasklist.begin(), render_tasklist.end(), [id = pt.task_id](AppTask const& t){ return t.task_id == id; }), render_tasklist.end()); render_tasklist.push_back(std::move(pt)); } if (dispatch.notify_worker) render_cv.notify_all(); } return f; } template void render_task(T task) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_render_thread(), false, 0U, render_running, true, false); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(render_task_mutex); render_tasklist.push_back(std::move(pt)); } if (dispatch.notify_worker) render_cv.notify_all(); } if (dispatch.wait_for_completion) f.get(); } void render_sync() { render_task([] {}); } ////////////////////////////////////////////////////////////////////////// // UI THREAD ////////////////////////////////////////////////////////////////////////// static std::deque ui_tasklist; static std::mutex ui_task_mutex; static std::condition_variable ui_cv; static std::thread ui_thread; static std::thread::id ui_thread_id; static bool ui_running; void ui_thread_tick(); void ui_thread_main(); void ui_thread_start(); void ui_thread_stop(); bool is_ui_thread() { return std::this_thread::get_id() == ui_thread_id; } // don't capture a reference to this ptr as the object may be destroyed // by the time the task is executed template std::future ui_task_async(T task, bool unique = false) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_ui_thread(), unique, 0U, ui_running, false, false); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(ui_task_mutex); const auto queue_dispatch = pp::app::plan_app_task_dispatch( false, unique, ui_tasklist.size(), ui_running, false, false); // remove any previously queued task from the same lambda if (queue_dispatch.remove_matching_unique_task) ui_tasklist.erase(std::remove_if(ui_tasklist.begin(), ui_tasklist.end(), [id = pt.task_id](AppTask const& t){ return t.task_id == id; }), ui_tasklist.end()); ui_tasklist.push_back(std::move(pt)); } if (dispatch.notify_worker) ui_cv.notify_all(); } return f; } template void ui_task(T task) { AppTask pt(task); auto f = pt.get_future(); const auto dispatch = pp::app::plan_app_task_dispatch( is_ui_thread(), false, 0U, ui_running, true, true); if (dispatch.execute_immediately) { pt(); } else if (dispatch.queue_task) { { std::lock_guard lock(ui_task_mutex); ui_tasklist.push_back(std::move(pt)); } if (dispatch.notify_worker) ui_cv.notify_all(); } if (dispatch.wait_for_completion) f.get(); if (dispatch.request_redraw) redraw = true; } void ui_sync() { ui_task([] {}); } };