From ee2ce3802f0538aa1786abb9a268550034580db5 Mon Sep 17 00:00:00 2001 From: Bruno Date: Sat, 27 Mar 2021 13:03:22 +0100 Subject: [PATCH] Linux Dialogs in GTK for SNAP compatibility Discarding use of ZENITY under linux (previously used with the tinyfiledialog) because snapcraft makes it impossible to use :(. Reimplementation of GTK+ dialogs directly inside vimix code. Note: no changes for OSX. Complete cleanup of cmake file. --- CMakeLists.txt | 106 +++++----- DialogToolkit.cpp | 326 +++++++++++++++++++++++++++++ DialogToolkit.h | 24 +++ Log.cpp | 19 +- README.md | 8 + Resource.cpp | 3 - UserInterfaceManager.cpp | 111 +++------- cmake/modules/FindGTK.cmake | 147 +++++++++++++ ext/OSCPack/ip/posix/UdpSocket.cpp | 5 +- snap/gui/vimix.desktop | 1 - snap/gui/vimix.svg | 67 ++++-- snap/snapcraft.yaml | 11 +- 12 files changed, 650 insertions(+), 178 deletions(-) create mode 100644 DialogToolkit.cpp create mode 100644 DialogToolkit.h create mode 100644 cmake/modules/FindGTK.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e6af93..2476f85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.8.0) +cmake_minimum_required(VERSION 3.8.2) project(vimix VERSION 0.0.1 LANGUAGES CXX C) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -46,6 +46,9 @@ if(UNIX) set(OpenGL_GL_PREFERENCE "GLVND") + find_package(GTK 3.0 REQUIRED) + macro_log_feature(GTK_FOUND "GTK" "GTK cross-platform widget toolkit" "http://www.gtk.org" TRUE) + endif() add_definitions(-DUNIX) elseif(WIN32) @@ -54,9 +57,21 @@ elseif(WIN32) endif() -# Include the CMake RC module -include(CMakeRC) +# Basics +set(CMAKE_THREAD_PREFER_PTHREAD TRUE) +find_package(Threads REQUIRED) +find_package(GLIB2) +macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org" TRUE) + +find_package(GObject) +macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org" TRUE) + +find_package(PNG REQUIRED) +macro_log_feature(PNG_FOUND "PNG" "Portable Network Graphics" "http://www.libpng.org" TRUE) + +find_package(ICU REQUIRED COMPONENTS i18n io uc) +macro_log_feature(ICU_FOUND "ICU" "International Components for Unicode" "http://site.icu-project.org" TRUE) # # GSTREAMER @@ -91,7 +106,6 @@ macro_log_feature(GSTREAMER_GL_LIBRARY_FOUND "GStreamer opengl library" "${GSTREAMER_GL_LIBRARY}" "http://gstreamer.freedesktop.org/" TRUE "1.0.0") - #find_package(GStreamerPluginsBad 1.0.0 COMPONENTS player) #macro_log_feature(GSTREAMER_PLAYER_LIBRARY_FOUND "GStreamer player library" #"${GSTREAMER_PLAYER_LIBRARY}" @@ -101,22 +115,6 @@ macro_log_feature(GSTREAMER_GL_LIBRARY_FOUND "GStreamer opengl library" # Various preprocessor definitions for GST add_definitions(-DGST_DISABLE_XML -DGST_DISABLE_LOADSAVE) -# Basics -find_package(GLIB2) -macro_log_feature(GLIB2_FOUND "GLib" "GTK general-purpose utility library" "http://www.gtk.org/" TRUE) - -find_package(GObject) -macro_log_feature(GOBJECT_FOUND "GObject" "GTK object-oriented framework" "http://www.gtk.org/" TRUE) - -set(CMAKE_THREAD_PREFER_PTHREAD TRUE) -find_package(Threads REQUIRED) -set(THREAD_LIBRARY Threads::Threads) - -find_package(PNG REQUIRED) -set(PNG_LIBRARY PNG::PNG) - -find_package(ICU REQUIRED COMPONENTS i18n io uc) -macro_log_feature(ICU_FOUND "ICU" "International Components for Unicode" "http://site.icu-project.org" TRUE) # @@ -124,10 +122,10 @@ macro_log_feature(ICU_FOUND "ICU" "International Components for Unicode" "http:/ # NB: set glfw3_PATH to /usr/local/Cellar/glfw/3.3.2/lib/cmake/glfw3 # find_package(glfw3 3.2 REQUIRED) -macro_log_feature(glfw3_FOUND "GLFW3" "Open Source multi-platform library for OpenGL" "http://www.glfw.org/" TRUE) +macro_log_feature(glfw3_FOUND "GLFW3" "Open Source multi-platform library for OpenGL" "http://www.glfw.org" TRUE) set(GLFW_LIBRARY glfw) -#find_package(OpenGL REQUIRED) +macro_display_feature_log() # static sub packages in ext set(BUILD_STATIC_LIBS ON) @@ -136,14 +134,14 @@ set(BUILD_STATIC_LIBS ON) # GLM # add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/glm) -message(STATUS "Compiling 'GLM' OpenGL mathematics https://glm.g-truc.net.") +message(STATUS "Compiling 'GLM' OpenGL mathematics https://glm.g-truc.net") # # GLAD # set(GLAD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/glad/include) add_library(GLAD "${CMAKE_CURRENT_SOURCE_DIR}/ext/glad/src/glad.c") -message(STATUS "Compiling 'GLAD' Open source multi-language OpenGL loader https://glad.dav1d.de/ -- ${GLAD_INCLUDE_DIR}.") +message(STATUS "Compiling 'GLAD' Open source multi-language OpenGL loader https://glad.dav1d.de -- ${GLAD_INCLUDE_DIR}.") # # DEAR IMGUI @@ -198,7 +196,6 @@ set(OSCPACK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/OSCPack) add_library(OSCPACK "${OSCPACK_SRCS}") message(STATUS "Compiling 'OSCPack' from http://www.rossbencina.com/code/oscpack -- ${OSCPACK_INCLUDE_DIR}.") - # # STB # @@ -215,22 +212,29 @@ if(WIN32) endif( WIN32 ) # -# TINY FILE DIALOG +# FILE DIALOG: use tinyfiledialog for all except Linux # -set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd) -add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c") -message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.") +if(UNIX) + if (APPLE) + set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd) + add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c") + message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.") + set(TINYFD_LIBRARY TINYFD) + endif() +else() + set(TINYFD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd) + add_library(TINYFD "${CMAKE_CURRENT_SOURCE_DIR}/ext/tfd/tinyfiledialogs.c") + message(STATUS "Compiling 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git -- ${TINYFD_INCLUDE_DIR}.") + set(TINYFD_LIBRARY TINYFD) +endif() # -# OBJ LOADER +# OBJ LOADER - not used # #set(OBJLOADER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/obj) #add_library(OBJLOADER "${CMAKE_CURRENT_SOURCE_DIR}/ext/obj/ObjLoader.cpp") #message(STATUS "Compiling 'ObjLoader' from https://github.com/mortennobel/OpenGL_3_2_Utils -- ${OBJLOADER_INCLUDE_DIR}.") -# find_package(PkgConfig REQUIRED) -# pkg_check_modules(GTK3 REQUIRED gtk+-3.0) - # # Application # @@ -316,6 +320,7 @@ set(VMIX_SRCS GstToolkit.cpp GlmToolkit.cpp SystemToolkit.cpp + DialogToolkit.cpp tinyxml2Toolkit.cpp NetworkToolkit.cpp Connection.cpp @@ -426,6 +431,11 @@ set(VMIX_RSC_FILES ./rsc/mesh/h_mark.ply ) +# Include the CMake RC module +include(CMakeRC) +cmrc_add_resource_library(vmix-resources ALIAS vmix::rc NAMESPACE vmix WHENCE rsc ${VMIX_RSC_FILES}) +message(STATUS "Using 'CMakeRC ' from https://github.com/vector-of-bool/cmrc.git -- ${CMAKE_MODULE_PATH}.") + ### DEFINE THE TARGET (OS specific) IF(APPLE) @@ -455,17 +465,19 @@ IF(APPLE) ELSE(APPLE) + link_directories (${GTK3_LIBRARY_DIRS}) + add_executable(${VMIX_BINARY} ${VMIX_SRCS} ${IMGUITEXTEDIT_SRC} ) - set(PLATFORM_LIBS "" + set(PLATFORM_LIBS + GTK::GTK ) ENDIF(APPLE) - ### COMPILE THE TARGET (all OS) set_property(TARGET ${VMIX_BINARY} PROPERTY CXX_STANDARD 17) @@ -473,14 +485,12 @@ set_property(TARGET ${VMIX_BINARY} PROPERTY C_STANDARD 11) target_compile_definitions(${VMIX_BINARY} PUBLIC "IMGUI_IMPL_OPENGL_LOADER_GLAD") -cmrc_add_resource_library(vmix-resources ALIAS vmix::rc NAMESPACE vmix WHENCE rsc ${VMIX_RSC_FILES}) -message(STATUS "Using 'CMakeRC ' from https://github.com/vector-of-bool/cmrc.git -- ${CMAKE_MODULE_PATH}.") - - target_link_libraries(${VMIX_BINARY} LINK_PRIVATE - ${GLFW_LIBRARY} GLAD - glm::glm + TINYXML2 + IMGUI + OSCPACK + ${GLFW_LIBRARY} ${CMAKE_DL_LIBS} ${GOBJECT_LIBRARIES} ${GSTREAMER_LIBRARY} @@ -491,21 +501,17 @@ target_link_libraries(${VMIX_BINARY} LINK_PRIVATE ${GSTREAMER_PBUTILS_LIBRARY} ${GSTREAMER_GL_LIBRARY} ${GSTREAMER_PLAYER_LIBRARY} - ${NFD_LIBRARY} - ${PNG_LIBRARY} - ${THREAD_LIBRARY} - TINYXML2 - TINYFD - IMGUI - OSCPACK - vmix::rc + ${TINYFD_LIBRARY} + Threads::Threads + PNG::PNG + glm::glm ICU::i18n ICU::io ICU::uc + vmix::rc ${PLATFORM_LIBS} ) -macro_display_feature_log() ### DEFINE THE PACKAGING (all OS) diff --git a/DialogToolkit.cpp b/DialogToolkit.cpp new file mode 100644 index 0000000..f98e0c6 --- /dev/null +++ b/DialogToolkit.cpp @@ -0,0 +1,326 @@ + +// multiplatform implementation of system dialogs +// +// 'TinyFileDialog' from https://github.com/native-toolkit/tinyfiledialogs.git +// is the prefered solution, but it is not compatible with linux snap packaging +// because of 'zenity' access rights nightmare :( +// Thus this re-implementation of native GTK+ dialogs for linux + +#if defined(LINUX) +#define USE_TINYFILEDIALOG 0 +#else +#define USE_TINYFILEDIALOG 1 +#endif + +#if USE_TINYFILEDIALOG +#include +#else +#include +#include +#endif + +#include "defines.h" +#include "SystemToolkit.h" +#include "DialogToolkit.h" + +bool gtk_init_ok = false; + +void add_filter_any_file_dialog( GtkWidget *dialog) +{ + /* append a wildcard option */ + GtkFileFilter *filter; + filter = gtk_file_filter_new(); + gtk_file_filter_set_name( filter, "Any file" ); + gtk_file_filter_add_pattern( filter, "*" ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); +} + +void add_filter_file_dialog( GtkWidget *dialog, int const countfilterPatterns, char const * const * const filterPatterns, char const * const filterDescription) +{ + GtkFileFilter *filter; + filter = gtk_file_filter_new(); + gtk_file_filter_set_name( filter, filterDescription ); + for (int i = 0; i < countfilterPatterns; i++ ) + gtk_file_filter_add_pattern( filter, filterPatterns[i] ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); +} + +void wait_for_event(void) +{ + while ( gtk_events_pending() ) + gtk_main_iteration(); +} + +bool gtk_init() +{ + if (!gtk_init_ok) { + if ( gtk_init_check( NULL, NULL ) ) + gtk_init_ok = true; + } + return gtk_init_ok; +} + +std::string DialogToolkit::saveSessionFileDialog(const std::string &path) +{ + std::string filename = ""; + char const * save_pattern[1] = { "*.mix" }; + +#if USE_TINYFILEDIALOG + char const * save_file_name; + + save_file_name = tinyfd_saveFileDialog( "Save a session file", path.c_str(), 1, save_pattern, "vimix session"); + + if (save_file_name) { + filename = std::string(save_file_name); + std::string extension = filename.substr(filename.find_last_of(".") + 1); + if (extension != "mix") + filename += ".mix"; + } +#else + if (!gtk_init()) { + ErrorDialog("Could not initialize GTK+ for dialog"); + return filename; + } + + GtkWidget *dialog = gtk_file_chooser_dialog_new( "Save Session File", NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, NULL ); + gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); + + // set file filters + add_filter_file_dialog(dialog, 1, save_pattern, "vimix session"); + add_filter_any_file_dialog(dialog); + + // Set the default path + gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), path.c_str() ); + + // ensure front and centered + gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE ); + static int x = 0, y = 0; + if (x != 0) + gtk_window_move( GTK_WINDOW(dialog), x, y); + + // display and get filename + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { + + char *save_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + if (save_file_name) + filename = std::string(save_file_name); + g_free( save_file_name ); + } + + // remember position + gtk_window_get_position( GTK_WINDOW(dialog), &x, &y); + + // done + gtk_widget_destroy(dialog); +// wait_for_event(); +#endif + + return filename; +} + + +std::string DialogToolkit::openSessionFileDialog(const std::string &path) +{ + std::string filename = ""; + std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); + char const * open_pattern[1] = { "*.mix" }; + +#if USE_TINYFILEDIALOG + char const * open_file_name; + open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 1, open_pattern, "vimix session", 0); + + if (open_file_name) + filename = std::string(open_file_name); +#else + if (!gtk_init()) { + ErrorDialog("Could not initialize GTK+ for dialog"); + return filename; + } + + GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Session File", NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, NULL ); + + // set file filters + add_filter_file_dialog(dialog, 1, open_pattern, "vimix session"); + add_filter_any_file_dialog(dialog); + + // Set the default path + gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() ); + + // ensure front and centered + gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE ); + static int x = 0, y = 0; + if (x != 0) + gtk_window_move( GTK_WINDOW(dialog), x, y); + + // display and get filename + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { + + char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + if (open_file_name) + filename = std::string(open_file_name); + g_free( open_file_name ); + } + + // remember position + gtk_window_get_position( GTK_WINDOW(dialog), &x, &y); + + // done + gtk_widget_destroy(dialog); +// wait_for_event(); +#endif + + return filename; +} + + +std::string DialogToolkit::ImportFileDialog(const std::string &path) +{ + std::string filename = ""; + std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); + char const * open_pattern[18] = { "*.mix", "*.mp4", "*.mpg", + "*.avi", "*.mov", "*.mkv", + "*.webm", "*.mod", "*.wmv", + "*.mxf", "*.ogg", "*.flv", + "*.asf", "*.jpg", "*.png", + "*.gif", "*.tif", "*.svg" }; +#if USE_TINYFILEDIALOG + char const * open_file_name; + open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0); + + if (open_file_name) + filename = std::string(open_file_name); +#else + + if (!gtk_init()) { + ErrorDialog("Could not initialize GTK+ for dialog"); + return filename; + } + + GtkWidget *dialog = gtk_file_chooser_dialog_new( "Import Media File", NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, NULL ); + + // set file filters + add_filter_file_dialog(dialog, 18, open_pattern, "All supported formats"); + add_filter_any_file_dialog(dialog); + + // Set the default path + gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() ); + + // ensure front and centered + gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE ); + static int x = 0, y = 0; + if (x != 0) + gtk_window_move( GTK_WINDOW(dialog), x, y); + + // display and get filename + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { + + char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + if (open_file_name) + filename = std::string(open_file_name); + g_free( open_file_name ); + } + + // remember position + gtk_window_get_position( GTK_WINDOW(dialog), &x, &y); + + // done + gtk_widget_destroy(dialog); + // wait_for_event(); +#endif + + return filename; +} + +std::string DialogToolkit::FolderDialog(const std::string &path) +{ + std::string foldername = ""; + std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); + +#if USE_TINYFILEDIALOG + char const * open_folder_name; + open_folder_name = tinyfd_selectFolderDialog("Select folder", startpath.c_str()); + + if (open_folder_name) + foldername = std::string(open_folder_name); +#else + if (!gtk_init()) { + ErrorDialog("Could not initialize GTK+ for dialog"); + return foldername; + } + + GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select folder", NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Select", GTK_RESPONSE_ACCEPT, NULL ); + gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); + + // Set the default path + gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() ); + + // ensure front and centered + gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE ); + static int x = 0, y = 0; + if (x != 0) + gtk_window_move( GTK_WINDOW(dialog), x, y); + + // display and get filename + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) { + + char *open_folder_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + if (open_folder_name) + foldername = std::string(open_folder_name); + g_free( open_folder_name ); + } + + // remember position + gtk_window_get_position( GTK_WINDOW(dialog), &x, &y); + + // done + gtk_widget_destroy(dialog); +// wait_for_event(); +#endif + + return foldername; +} + + +void DialogToolkit::ErrorDialog(const char* message) +{ +#if USE_TINYFILEDIALOG + tinyfd_messageBox( APP_TITLE, message, "ok", "error", 0); +#else + if (!gtk_init()) { + return; + } + + GtkWidget *dialog = gtk_message_dialog_new( NULL, GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + "Error: %s", message); + + gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE ); + static int x = 0, y = 0; + if (x != 0) + gtk_window_move( GTK_WINDOW(dialog), x, y); + + // show + gtk_dialog_run( GTK_DIALOG(dialog) ); + + // remember position + gtk_window_get_position( GTK_WINDOW(dialog), &x, &y); + + // done + gtk_widget_destroy( dialog ); +// wait_for_event(); +#endif +} + + diff --git a/DialogToolkit.h b/DialogToolkit.h new file mode 100644 index 0000000..bb2b6a0 --- /dev/null +++ b/DialogToolkit.h @@ -0,0 +1,24 @@ +#ifndef DIALOGTOOLKIT_H +#define DIALOGTOOLKIT_H + +#include + + +namespace DialogToolkit +{ + +std::string saveSessionFileDialog(const std::string &path); + +std::string openSessionFileDialog(const std::string &path); + +std::string ImportFileDialog(const std::string &path); + +std::string FolderDialog(const std::string &path); + +void ErrorDialog(const char* message); + +} + + + +#endif // DIALOGTOOLKIT_H diff --git a/Log.cpp b/Log.cpp index 20bce45..01d1f35 100644 --- a/Log.cpp +++ b/Log.cpp @@ -1,4 +1,7 @@ -#include "Log.h" +#include +#include +#include +using namespace std; #include "imgui.h" #ifndef IMGUI_DEFINE_MATH_OPERATORS @@ -6,16 +9,11 @@ #endif #include "imgui_internal.h" -#include "ImGuiToolkit.h" #include "defines.h" +#include "ImGuiToolkit.h" +#include "DialogToolkit.h" +#include "Log.h" -// multiplatform -#include - -#include -#include -#include -using namespace std; static std::mutex mtx; @@ -296,7 +294,8 @@ void Log::Error(const char* fmt, ...) buf.appendfv(fmt, args); va_end(args); - tinyfd_messageBox( APP_TITLE, buf.c_str(), "ok", "error", 0); + DialogToolkit::ErrorDialog(buf.c_str()); + Log::Info("Error - %s\n", buf.c_str()); } diff --git a/README.md b/README.md index 24527f7..1f99fd8 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,11 @@ and (recursively) clone all the internal git Dependencies. **OSX with Brew** brew install cmake libpng glfw gstreamer gst-libav gst-plugins-bad gst-plugins-base gst-plugins-good gst-plugins-ugly icu4c + + +#### Generate snap + +From vimix root directory + snapcraft --debug + snap install --dangerous vimix_0.5_amd64.snap + diff --git a/Resource.cpp b/Resource.cpp index bd0bda9..dd4d249 100644 --- a/Resource.cpp +++ b/Resource.cpp @@ -10,9 +10,6 @@ // Desktop OpenGL function loader #include -// multiplatform message box -#include - // standalone image loader #include "stb_image.h" diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 44986f8..9e58fcf 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -20,9 +20,6 @@ using namespace std; // Include glfw3.h after our OpenGL definitions #include -// multiplatform -#include - #include #include #include @@ -38,17 +35,17 @@ using namespace std; #include "defines.h" #include "Log.h" #include "SystemToolkit.h" +#include "DialogToolkit.h" +#include "GlmToolkit.h" +#include "GstToolkit.h" +#include "ImGuiToolkit.h" +#include "ImGuiVisitor.h" #include "RenderingManager.h" #include "Connection.h" #include "ActionManager.h" #include "Resource.h" -#include "FileDialog.h" #include "Settings.h" #include "SessionCreator.h" -#include "ImGuiToolkit.h" -#include "ImGuiVisitor.h" -#include "GlmToolkit.h" -#include "GstToolkit.h" #include "Mixer.h" #include "Recorder.h" #include "Streamer.h" @@ -78,79 +75,12 @@ void ShowSandbox(bool* p_open); const std::chrono::milliseconds timeout = std::chrono::milliseconds(4); static std::atomic fileDialogPending_ = false; - static std::vector< std::future > saveSessionFileDialogs; -static std::string saveSessionFileDialog(const std::string &path) -{ - std::string filename = ""; - char const * save_file_name; - char const * save_pattern[1] = { "*.mix" }; - - save_file_name = tinyfd_saveFileDialog( "Save a session file", path.c_str(), 1, save_pattern, "vimix session"); - - if (save_file_name) { - filename = std::string(save_file_name); - std::string extension = filename.substr(filename.find_last_of(".") + 1); - if (extension != "mix") - filename += ".mix"; - } - - return filename; -} - static std::vector< std::future > openSessionFileDialogs; static std::vector< std::future > importSessionFileDialogs; -static std::string openSessionFileDialog(const std::string &path) -{ - std::string filename = ""; - std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); - - char const * open_pattern[18] = { "*.mix" }; - char const * open_file_name; - open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 1, open_pattern, "vimix session", 0); - - if (open_file_name) - filename = std::string(open_file_name); - - return filename; -} - static std::vector< std::future > fileImportFileDialogs; -static std::string ImportFileDialog(const std::string &path) -{ - std::string filename = ""; - std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); - - char const * open_pattern[18] = { "*.mix", "*.mp4", "*.mpg", - "*.avi", "*.mov", "*.mkv", - "*.webm", "*.mod", "*.wmv", - "*.mxf", "*.ogg", "*.flv", - "*.asf", "*.jpg", "*.png", - "*.gif", "*.tif", "*.svg" }; - char const * open_file_name; - open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0); - - if (open_file_name) - filename = std::string(open_file_name); - - return filename; -} - static std::vector< std::future > recentFolderFileDialogs; static std::vector< std::future > recordFolderFileDialogs; -static std::string FolderDialog(const std::string &path) -{ - std::string foldername = ""; - std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); - - char const * open_folder_name; - open_folder_name = tinyfd_selectFolderDialog("Select a folder", startpath.c_str()); - - if (open_folder_name) - foldername = std::string(open_folder_name); - - return foldername; -} UserInterface::UserInterface() @@ -700,7 +630,7 @@ void UserInterface::selectSaveFilename() { // launch file dialog to select a session filename to save if ( saveSessionFileDialogs.empty()) { - saveSessionFileDialogs.emplace_back( std::async(std::launch::async, saveSessionFileDialog, Settings::application.recentSessions.path) ); + saveSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::saveSessionFileDialog, Settings::application.recentSessions.path) ); fileDialogPending_ = true; } navigator.hidePannel(); @@ -710,7 +640,7 @@ void UserInterface::selectOpenFilename() { // launch file dialog to select a session filename to open if ( openSessionFileDialogs.empty()) { - openSessionFileDialogs.emplace_back( std::async(std::launch::async, openSessionFileDialog, Settings::application.recentSessions.path) ); + openSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openSessionFileDialog, Settings::application.recentSessions.path) ); fileDialogPending_ = true; } navigator.hidePannel(); @@ -906,7 +836,7 @@ void UserInterface::showMenuFile() if (ImGui::MenuItem( ICON_FA_FILE_EXPORT " Import")) { // launch file dialog to open a session file if ( importSessionFileDialogs.empty()) { - importSessionFileDialogs.emplace_back( std::async(std::launch::async, openSessionFileDialog, Settings::application.recentSessions.path) ); + importSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openSessionFileDialog, Settings::application.recentSessions.path) ); fileDialogPending_ = true; } navigator.hidePannel(); @@ -1288,7 +1218,7 @@ void UserInterface::RenderPreview() ImGui::Combo("Path", &selected_path, name_path, 4); if (selected_path > 2) { if (recordFolderFileDialogs.empty()) { - recordFolderFileDialogs.emplace_back( std::async(std::launch::async, FolderDialog, Settings::application.record.path) ); + recordFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.record.path) ); fileDialogPending_ = true; } } @@ -2490,7 +2420,7 @@ void Navigator::RenderNewPannel() if ( ImGui::Button( ICON_FA_FILE_EXPORT " Open file", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) { // launch async call to file dialog and get its future. if (fileImportFileDialogs.empty()) { - fileImportFileDialogs.emplace_back( std::async(std::launch::async, ImportFileDialog, Settings::application.recentImport.path) ); + fileImportFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::ImportFileDialog, Settings::application.recentImport.path) ); fileDialogPending_ = true; } } @@ -2776,7 +2706,7 @@ void Navigator::RenderMainPannel() // Option 2 : add a folder if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") ){ if (recentFolderFileDialogs.empty()) { - recentFolderFileDialogs.emplace_back( std::async(std::launch::async, FolderDialog, Settings::application.recentFolders.path) ); + recentFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::FolderDialog, Settings::application.recentFolders.path) ); fileDialogPending_ = true; } } @@ -3023,6 +2953,8 @@ int hover(const char *label) #define SEGMENT_ARRAY_MAX 1000 +#define MAXSIZE 65535 + void ShowSandbox(bool* p_open) { @@ -3036,6 +2968,16 @@ void ShowSandbox(bool* p_open) ImGui::Text("Testing sandox"); + if (ImGui::Button("Message test")) { + Log::Error("Testing dialog"); + } + + static char str[128] = ""; + ImGui::InputText("Command", str, IM_ARRAYSIZE(str)); + if ( ImGui::Button("Execute") ) + SystemToolkit::execute(str); + + // const guint64 duration = GST_SECOND * 6; // const guint64 step = GST_MSECOND * 20; // static guint64 t = 0; @@ -3121,8 +3063,9 @@ void ShowSandbox(bool* p_open) // ImGui::EndChild(); + static char str0[128] = "àöäüèáû вторая строчка"; - ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); + ImGui::InputText("##inputtext", str0, IM_ARRAYSIZE(str0)); std::string tra = SystemToolkit::transliterate(std::string(str0)); ImGui::Text("Transliteration: '%s'", tra.c_str()); @@ -3159,8 +3102,8 @@ void UserInterface::RenderAbout(bool* p_open) ImGui::Spacing(); ImGui::Text("\nvimix is built using the following libraries:"); - tinyfd_inputBox("tinyfd_query", NULL, NULL); - ImGui::Text("- Tinyfiledialogs v%s mode '%s'", tinyfd_version, tinyfd_response); +// tinyfd_inputBox("tinyfd_query", NULL, NULL); +// ImGui::Text("- Tinyfiledialogs v%s mode '%s'", tinyfd_version, tinyfd_response); ImGui::Columns(3, "abouts"); ImGui::Separator(); diff --git a/cmake/modules/FindGTK.cmake b/cmake/modules/FindGTK.cmake new file mode 100644 index 0000000..0faeaf0 --- /dev/null +++ b/cmake/modules/FindGTK.cmake @@ -0,0 +1,147 @@ +# - Try to find GTK+ 3.x or 4.x +# +# Copyright (C) 2012 Raphael Kubo da Costa +# Copyright (C) 2013, 2015, 2020 Igalia S.L. +# +# 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 HOLDER AND ITS 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 ITS +# 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. + +#[=======================================================================[.rst: +FindGTK +------- + +Find GTK headers and libraries. + +Optional Components +^^^^^^^^^^^^^^^^^^^ + +The ``COMPONENTS`` (or ``OPTIONAL_COMPONENTS``) keyword can be passed to +``find_package()``, the following GTK components can be searched for: + +- ``unix-print`` + + +Imported Targets +^^^^^^^^^^^^^^^^ + +``GTK::GTK`` + The GTK library, if found. +``GTK::UnixPrint`` + The GTK unix-print library, if found. + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables in your project: + +``GTK_FOUND`` + true if (the requested version of) GTK is available. +``GTK_UNIX_PRINT_FOUND`` + true if the ``unix-print`` component is available. +``GTK_4`` + whether GTK 4 was detected +``GTK_3`` + whether GTK 3 was detected +``GTK_VERSION`` + the version of GTK. +``GTK_SUPPORTS_BROADWAY`` + true if the Broadway target is built into GTK. +``GTK_SUPPORTS_QUARTZ`` + true if the Quartz target is built into GTK. +``GTK_SUPPORTS_WAYLAND`` + true if the Wayland target is built into GTK. +``GTK_SUPPORTS_WIN32`` + true if the Windows target is built into GTK. +``GTK_SUPPORTS_X11`` + true if the X11 target is built into GTK. + +#]=======================================================================] + + +if (GTK_FIND_VERSION VERSION_LESS 3.90) + set(GTK_PC_MODULE "gtk+-3.0") + set(GTK_PC_UNIX_PRINT_MODULE "gtk+-unix-print-3.0") + set(GTK_4 FALSE) + set(GTK_3 TRUE) +else () + set(GTK_PC_MODULE "gtk4") + set(GTK_PC_UNIX_PRINT_MODULE "gtk4-unix-print") + set(GTK_4 TRUE) + set(GTK_3 FALSE) +endif () + +find_package(PkgConfig QUIET) +pkg_check_modules(GTK IMPORTED_TARGET ${GTK_PC_MODULE}) + +set(GTK_VERSION_OK TRUE) +if (GTK_VERSION) + if (GTK_FIND_VERSION_EXACT) + if (NOT("${GTK_FIND_VERSION}" VERSION_EQUAL "${GTK_VERSION}")) + set(GTK_VERSION_OK FALSE) + endif () + else () + if ("${GTK_VERSION}" VERSION_LESS "${GTK_FIND_VERSION}") + set(GTK_VERSION_OK FALSE) + endif () + endif () +endif () + +# Set all the GTK_SUPPORTS_ variables to FALSE initially. +foreach (gtk_target broadway quartz wayland win32 x11) + string(TOUPPER "GTK_SUPPORTS_${gtk_target}" gtk_target) + set(${gtk_target} FALSE) +endforeach () + +if (GTK_VERSION AND GTK_VERSION_OK) + # Fetch the "targets" variable and set GTK_SUPPORTS_. + pkg_get_variable(GTK_TARGETS ${GTK_PC_MODULE} targets) + separate_arguments(GTK_TARGETS) + foreach (gtk_target ${GTK_TARGETS}) + string(TOUPPER "GTK_SUPPORTS_${gtk_target}" gtk_target) + set(${gtk_target} TRUE) + endforeach () +endif () + +if (TARGET PkgConfig::GTK AND NOT TARGET GTK::GTK) + add_library(GTK::GTK INTERFACE IMPORTED GLOBAL) + set_property(TARGET GTK::GTK PROPERTY + INTERFACE_LINK_LIBRARIES PkgConfig::GTK + ) +endif () + +# Try to find additional components +foreach (gtk_component ${GTK_FIND_COMPONENTS}) + if (NOT "${gtk_component}" STREQUAL unix-print) + message(FATAL_ERROR "Invalid component name: ${gtk_component}") + endif () + pkg_check_modules(GTK_UNIX_PRINT IMPORTED_TARGET "${GTK_PC_UNIX_PRINT_MODULE}") + if (GTK_FIND_REQUIRED_unix-print AND NOT GTK_UNIX_PRINT_FOUND) + message(FATAL_ERROR "Component unix-print not found") + endif () + if (TARGET PkgConfig::GTK_UNIX_PRINT AND NOT TARGET GTK::UnixPrint) + add_library(GTK::UnixPrint INTERFACE IMPORTED GLOBAL) + set_property(TARGET GTK::UnixPrint PROPERTY + INTERFACE_LINK_LIBRARIES PkgConfig::GTK_UNIX_PRINT) + endif () +endforeach () + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK DEFAULT_MSG GTK_VERSION GTK_VERSION_OK) diff --git a/ext/OSCPack/ip/posix/UdpSocket.cpp b/ext/OSCPack/ip/posix/UdpSocket.cpp index 4fa6204..d099494 100644 --- a/ext/OSCPack/ip/posix/UdpSocket.cpp +++ b/ext/OSCPack/ip/posix/UdpSocket.cpp @@ -479,7 +479,7 @@ public: if( FD_ISSET( breakPipe_[0], &tempfds ) ){ // clear pending data from the asynchronous break pipe char c; - read( breakPipe_[0], &c, 1 ); + size_t n = read( breakPipe_[0], &c, 1 ); } if( break_ ) @@ -534,7 +534,8 @@ public: break_ = true; // Send a termination message to the asynchronous break pipe, so select() will return - write( breakPipe_[1], "!", 1 ); + if (write( breakPipe_[1], "!", 1 ) < 1) + throw std::runtime_error("AsynchronousBreak failed\n"); } }; diff --git a/snap/gui/vimix.desktop b/snap/gui/vimix.desktop index ce21dbc..d19a49e 100644 --- a/snap/gui/vimix.desktop +++ b/snap/gui/vimix.desktop @@ -5,5 +5,4 @@ Comment=Live video mixing. Categories=AudioVideo;Video;Graphics;Utility; Icon=${SNAP}/meta/gui/vimix.svg Exec=vimix -TryExec=vimix Terminal=false diff --git a/snap/gui/vimix.svg b/snap/gui/vimix.svg index 25b1743..00f4fe0 100644 --- a/snap/gui/vimix.svg +++ b/snap/gui/vimix.svg @@ -1,6 +1,4 @@ - - + inkscape:showpageshadow="false" + inkscape:document-rotation="0"> + transform="translate(-0.8,48.8)" + style="opacity:0.89674699"> + + d="m 32.8,-48.060406 c -17.255744,0 -31.2604061,14.004662 -31.2604061,31.260406 0,17.25574393 14.0046621,31.260406 31.2604061,31.260406 17.255744,0 31.260406,-14.00466207 31.260406,-31.260406 0,-17.255744 -14.004662,-31.260406 -31.260406,-31.260406 z m 0,3.907551 c 15.098776,0 27.352855,12.254079 27.352855,27.352855 0,15.0987763 -12.254079,27.352856 -27.352855,27.352856 -15.098776,0 -27.3528552,-12.2540797 -27.3528552,-27.352856 0,-15.098776 12.2540792,-27.352855 27.3528552,-27.352855 z" + style="display:inline;overflow:visible;visibility:visible;opacity:0.2;fill:#848484;fill-opacity:0.654019;fill-rule:nonzero;stroke:none;stroke-width:4.34172;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.6;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;enable-background:accumulate" + inkscape:connector-curvature="0" + inkscape:export-xdpi="405" + inkscape:export-ydpi="405" /> + + + + style="opacity:0.809083;fill:#888888;fill-opacity:1;stroke:#ffffff;stroke-width:0.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" + id="path859" + sodipodi:type="arc" + sodipodi:cx="32.47654" + sodipodi:cy="-43.181934" + sodipodi:rx="2.0615652" + sodipodi:ry="2.0901108" + sodipodi:start="0" + sodipodi:end="6.2616827" + sodipodi:arc-type="chord" + d="m 34.538105,-43.181934 a 2.0615652,2.0901108 0 0 1 -2.050483,2.09008 2.0615652,2.0901108 0 0 1 -2.072528,-2.067609 2.0615652,2.0901108 0 0 1 2.0282,-2.11231 2.0615652,2.0901108 0 0 1 2.094334,2.044899 z" + sodipodi:open="true" /> diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 596337f..8097b34 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -37,15 +37,18 @@ apps: GST_PLUGIN_SYSTEM_PATH : $SNAP/usr/lib/x86_64-linux-gnu/gstreamer-1.0 GST_PLUGIN_SCANNER: $SNAP/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner + parts: vimix-binary: plugin: cmake source: . configflags: - - -DCMAKE_BUILD_TYPE=RelWithDebInfo + - -DCMAKE_INSTALL_PREFIX=/ + - -DCMAKE_BUILD_TYPE=RelWithDebInfo build-packages: - g++ - make + - git - libpng-dev - libxrandr-dev - libglfw3-dev @@ -53,7 +56,6 @@ parts: - libgstreamer-plugins-base1.0-dev - libicu-dev stage-packages: - - zenity - libpng16-16 - libglu1-mesa - freeglut3 @@ -69,11 +71,6 @@ parts: - gstreamer1.0-plugins-ugly - libgpm2 - libslang2 - stage-snaps: - - zenity-integration - - -