From 33ad240c2ca78a3dcde9a41176ac3f787fd09724 Mon Sep 17 00:00:00 2001 From: Adrian Iain Lam Date: Fri, 1 Jan 2021 11:36:37 +0000 Subject: [PATCH] Add GUI control panel (first draft) --- CMakeLists.txt | 8 +- README.md | 8 +- example/demo.patch | 21 +- include/mouse_cursor_tracker.h | 25 + src/gui.glade | 3103 ++++++++++++++++++++++++++++++++++++++ src/mouse_cursor_tracker.cpp | 9 +- src/mouse_cursor_tracker_gui.cpp | 378 +++++ 7 files changed, 3540 insertions(+), 12 deletions(-) create mode 100644 src/gui.glade create mode 100644 src/mouse_cursor_tracker_gui.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d95009..5f591f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,9 @@ project(MouseTrackerForCubism_project) find_library(xdo_LIBS NAMES xdo PATHS /usr/lib REQUIRED) find_library(pulse_LIBS NAMES pulse PATHS /usr/lib REQUIRED) +find_package(PkgConfig) +pkg_check_modules(GTKMM gtkmm-3.0) + include(ExternalProject) ExternalProject_Add(editline SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/editline/src @@ -15,11 +18,12 @@ ExternalProject_Add(editline ExternalProject_Get_Property(editline install_dir) set(editline_INSTALL_DIR ${install_dir}) -include_directories(include ${CMAKE_CURRENT_SOURCE_DIR}/lib/editline/include) +include_directories(include ${CMAKE_CURRENT_SOURCE_DIR}/lib/editline/include ${GTKMM_INCLUDE_DIRS}) add_library( MouseTrackerForCubism STATIC src/mouse_cursor_tracker.cpp + src/mouse_cursor_tracker_gui.cpp ) set_target_properties( @@ -27,4 +31,4 @@ set_target_properties( include/mouse_cursor_tracker.h ) -target_link_libraries(MouseTrackerForCubism ${xdo_LIBS} ${pulse_LIBS} pulse-simple ${editline_INSTALL_DIR}/lib/libeditline.a) +target_link_libraries(MouseTrackerForCubism ${xdo_LIBS} ${pulse_LIBS} pulse-simple ${editline_INSTALL_DIR}/lib/libeditline.a ${GTKMM_LIBRARIES}) diff --git a/README.md b/README.md index 16a8c46..d6cb272 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,12 @@ if you don't have C++17 support. 1. Install dependencies. You will require a recent C/C++ compiler, `make`, `patch`, CMake >= 3.16, - libxdo, and PulseAudio. To compile the example + libxdo, PulseAudio, and a stable version of gtkmm 3. To compile the example program you will also require the OpenGL library (and its dev headers) among other libraries required for the example program. The libraries I had to install (this list may not be exhaustive) are: - libxdo-dev libpulse-dev libgl1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libglu1-mesa-dev + libxdo-dev libpulse-dev libgl1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libglu1-mesa-dev libgtkmm-3.0-dev 2. Clone this repository including its submodule (editline) @@ -123,7 +123,9 @@ The library itself is provided under the MIT license. By "the library itself" I refer to the following files that I have provided under this repo: * src/mouse_cursor_tracker.cpp - * include/mouse_cursor_tracker.cpp + * src/mouse_cursor_tracker_gui.cpp + * src/gui.glade + * include/mouse_cursor_tracker.h * and if you decide to build the binary for the library, the resulting binary file (typically build/libMouseTrackerForCubism.a) diff --git a/example/demo.patch b/example/demo.patch index 304b687..cfc3aac 100644 --- a/example/demo.patch +++ b/example/demo.patch @@ -1,6 +1,6 @@ diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt --- ./demo_clean/CMakeLists.txt 2020-10-01 22:47:25.846828066 +0100 -+++ ./demo_dev/CMakeLists.txt 2020-10-01 23:29:15.530233484 +0100 ++++ ./demo_dev/CMakeLists.txt 2021-01-01 11:11:14.995691070 +0000 @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16) # Set app name. set(APP_NAME Demo) @@ -19,17 +19,19 @@ diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -@@ -64,6 +64,9 @@ target_link_libraries(Framework Live2DCu +@@ -64,6 +64,11 @@ target_link_libraries(Framework Live2DCu # Find opengl libraries. find_package(OpenGL REQUIRED) +# Add MouseTrackerForCubism ++find_package(PkgConfig) ++pkg_check_modules(GTKMM gtkmm-3.0) +add_subdirectory(../.. MouseTrackerForCubism_build) + # Make executable app. add_executable(${APP_NAME}) # Add source files. -@@ -73,9 +76,11 @@ target_link_libraries(${APP_NAME} +@@ -73,9 +78,20 @@ target_link_libraries(${APP_NAME} Framework glfw ${OPENGL_LIBRARIES} @@ -38,13 +40,22 @@ diff -pruN --exclude build ./demo_clean/CMakeLists.txt ./demo_dev/CMakeLists.txt ) # Specify include directories. -target_include_directories(${APP_NAME} PRIVATE ${STB_PATH}) -+target_include_directories(${APP_NAME} PRIVATE ${STB_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../include) ++target_include_directories(${APP_NAME} PRIVATE ${STB_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/../../include ${GTKMM_INCLUDE_DIRS}) ++ ++# Copy GUI to build directory ++add_custom_command( ++ TARGET ${APP_NAME} ++ POST_BUILD ++ COMMAND ++ ${CMAKE_COMMAND} -E ++ copy ${CMAKE_CURRENT_SOURCE_DIR}/../../src/gui.glade $/gui.glade ++) # Copy resource directory to build directory. add_custom_command( diff -pruN --exclude build ./demo_clean/scripts/make_gcc ./demo_dev/scripts/make_gcc --- ./demo_clean/scripts/make_gcc 2020-10-01 22:47:25.854827921 +0100 -+++ ./demo_dev/scripts/make_gcc 2020-10-12 03:42:07.847955578 +0100 ++++ ./demo_dev/scripts/make_gcc 2021-01-01 11:34:25.583684883 +0000 @@ -10,4 +10,4 @@ BUILD_PATH=$SCRIPT_PATH/../build/make_gc cmake -S "$CMAKE_PATH" \ -B "$BUILD_PATH" \ diff --git a/include/mouse_cursor_tracker.h b/include/mouse_cursor_tracker.h index 6cd91c4..ca667a9 100644 --- a/include/mouse_cursor_tracker.h +++ b/include/mouse_cursor_tracker.h @@ -31,6 +31,13 @@ SOFTWARE. #include #include #include +#include +#include +#include +#include +#include +#include + extern "C" { #include @@ -113,8 +120,10 @@ private: std::thread m_getVolumeThread; std::thread m_parseCommandThread; + std::thread m_guiThread; void audioLoop(void); void cliLoop(void); + void guiLoop(void); void processCommand(std::string); double m_currentVol; pa_simple *m_pulse; @@ -127,6 +136,22 @@ private: void populateDefaultConfig(void); void parseConfig(std::string cfgPath); + + Glib::RefPtr m_gtkapp; + Glib::RefPtr m_builder; + + std::vector m_onClearResettingParams; + + // GUI signal handlers + void onMotionStartButton(void); + void onExpressionStartButton(void); + void onParamUpdateButton(Gtk::Scale *scale, bool isInc); + void onParamValChanged(Glib::RefPtr adj, std::string paramName); + void onAutoToggle(Gtk::CheckButton *check, Gtk::Scale *scale, + Gtk::Button *buttonDec, Gtk::Button *buttonInc, + std::string paramName); + void onClearButton(Glib::RefPtr button, std::string paramName, Glib::RefPtr adj); + void onExpanderChange(Gtk::Window *window); }; #endif diff --git a/src/gui.glade b/src/gui.glade new file mode 100644 index 0000000..0b73f7f --- /dev/null +++ b/src/gui.glade @@ -0,0 +1,3103 @@ + + + + + + -30 + 30 + 1 + 10 + + + -30 + 30 + 1 + 10 + + + -30 + 30 + 1 + 10 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + -10 + 10 + 1 + 5 + + + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -0.5 + 2 + 1 + 0.10000000000000001 + 0.5 + + + 1 + 0.10000000000000001 + 0.5 + + + -0.5 + 2 + 1 + 0.10000000000000001 + 0.5 + + + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + -1 + 1 + 0.10000000000000001 + 0.5 + + + 1.5 + 0.10000000000000001 + 0.5 + + + 10 + 1 + 5 + + + 1 + 0.10000000000000001 + 0.5 + + + 480 + False + Control panel - Mouse Tracker for Cubism + + + + + + True + False + vertical + + + True + False + + + True + False + True + + + 130 + True + False + + + False + True + 0 + + + + + 125 + True + False + 1 + priorityNormal + + Force + Normal + Idle + None + + + + False + True + 1 + + + + + 1 + 0 + + + + + Start + 120 + True + True + True + 60 + + + 2 + 0 + + + + + 160 + True + False + Motion + + + 0 + 0 + + + + + 160 + True + False + Expression + + + 0 + 1 + + + + + 250 + True + False + + + 1 + 1 + + + + + Start + 120 + True + True + True + 60 + + + 2 + 1 + + + + + False + True + 0 + + + + + True + True + + + True + False + + + Auto (mouse tracking) + True + True + False + True + True + + + 4 + 0 + + + + + 200 + True + False + True + adjAngleX + 1 + False + + + 2 + 0 + + + + + Auto (mouse tracking) + True + True + False + True + True + + + 4 + 1 + + + + + 200 + True + False + True + adjAngleY + 1 + False + + + 2 + 1 + + + + + 200 + True + True + adjAngleZ + 1 + False + + + 2 + 2 + + + + + 160 + True + False + Angle X + + + 0 + 0 + + + + + 160 + True + False + Angle Y + + + 0 + 1 + + + + + 160 + True + False + Angle Z + + + 0 + 2 + + + + + ← + 30 + True + False + True + True + + + 1 + 0 + + + + + ↓ + True + False + True + True + + + 1 + 1 + + + + + ↶ + True + True + True + + + 1 + 2 + + + + + ↑ + True + False + True + True + + + 3 + 1 + + + + + → + 30 + True + False + True + True + + + 3 + 0 + + + + + ↷ + True + True + True + + + 3 + 2 + + + + + Clear + 120 + True + True + 60 + + + 4 + 2 + + + + + + + True + False + Head + + + + + False + True + 1 + + + + + True + True + + + True + False + + + 160 + True + False + Left eye open + + + 0 + 0 + + + + + 160 + True + False + Left eye smile + + + 0 + 1 + + + + + 160 + True + False + Right eye open + + + 0 + 2 + + + + + 160 + True + False + Right eye smile + + + 0 + 3 + + + + + 160 + True + False + Eyeball X + + + 0 + 4 + + + + + 160 + True + False + Eyeball Y + + + 0 + 5 + + + + + 160 + True + False + Eyeball form + + + 0 + 6 + + + + + 200 + True + False + True + adjEyeROpen + 1 + False + + + 2 + 2 + + + + + 200 + True + True + adjEyeRSmile + 1 + False + + + 2 + 3 + + + + + 200 + True + True + adjEyeBallX + 1 + False + + + 2 + 4 + + + + + 200 + True + True + adjEyeBallY + 1 + False + + + 2 + 5 + + + + + 200 + True + True + adjEyeBallForm + 10 + 1 + False + + + 2 + 6 + + + + + Auto blink + True + True + False + 60 + True + True + + + 4 + 0 + + + + + 200 + True + False + True + adjEyeLOpen + 1 + False + + + 2 + 0 + + + + + 200 + True + True + adjEyeLSmile + 1 + False + + + 2 + 1 + + + + + Auto blink + True + True + False + 60 + True + True + + + 4 + 2 + + + + + – + 30 + True + False + True + True + + + 1 + 0 + + + + + – + True + True + True + + + 1 + 1 + + + + + – + True + True + True + + + 1 + 3 + + + + + – + True + False + True + True + + + 1 + 2 + + + + + ↓ + True + True + True + + + 1 + 5 + + + + + ← + True + True + True + + + 1 + 4 + + + + + – + True + True + True + + + 1 + 6 + + + + + + + True + True + True + + + 3 + 1 + + + + + + + 30 + True + False + True + True + + + 3 + 0 + + + + + + + True + False + True + True + + + 3 + 2 + + + + + → + True + True + True + + + 3 + 4 + + + + + + + True + True + True + + + 3 + 3 + + + + + ↑ + True + True + True + + + 3 + 5 + + + + + + + True + True + True + + + 3 + 6 + + + + + Clear + 120 + True + True + 60 + + + 4 + 1 + + + + + Clear + 120 + True + True + 60 + + + 4 + 3 + + + + + Clear + 120 + True + True + 60 + + + 4 + 5 + + + + + Clear + 120 + True + True + 60 + + + 4 + 4 + + + + + Clear + 120 + True + True + 60 + + + 4 + 6 + + + + + + + True + False + Eyes + + + + + False + True + 2 + + + + + True + True + + + True + False + + + 200 + True + True + adjBrowLX + 1 + False + + + 2 + 0 + + + + + 200 + True + True + adjBrowLY + 1 + False + + + 2 + 1 + + + + + 200 + True + True + adjBrowLAngle + 1 + False + + + 2 + 2 + + + + + 200 + True + True + adjBrowLForm + 1 + False + + + 2 + 3 + + + + + 200 + True + True + adjBrowRX + 1 + False + + + 2 + 4 + + + + + 200 + True + True + adjBrowRY + 1 + False + + + 2 + 5 + + + + + 200 + True + True + adjBrowRAngle + 1 + False + + + 2 + 6 + + + + + 200 + True + True + adjBrowRForm + 1 + False + + + 2 + 7 + + + + + → + 30 + True + True + True + + + 3 + 0 + + + + + ↑ + True + True + True + + + 3 + 1 + + + + + ↷ + True + True + True + + + 3 + 2 + + + + + + + True + True + True + + + 3 + 3 + + + + + ← + True + True + True + + + 3 + 4 + + + + + ↑ + True + True + True + + + 3 + 5 + + + + + ↶ + True + True + True + + + 3 + 6 + + + + + + + True + True + True + + + 3 + 7 + + + + + ← + 30 + True + True + True + + + 1 + 0 + + + + + ↶ + True + True + True + + + 1 + 2 + + + + + ↓ + True + True + True + + + 1 + 1 + + + + + – + True + True + True + + + 1 + 3 + + + + + → + True + True + True + + + 1 + 4 + + + + + ↓ + True + True + True + + + 1 + 5 + + + + + ↷ + True + True + True + + + 1 + 6 + + + + + – + True + True + True + + + 1 + 7 + + + + + 160 + True + False + Left eyebrow X + + + 0 + 0 + + + + + 160 + True + False + Left eyebrow Y + + + 0 + 1 + + + + + 160 + True + False + Left eyebrow angle + + + 0 + 2 + + + + + 160 + True + False + Left eyebrow form + + + 0 + 3 + + + + + 160 + True + False + RIght eyebrow X + + + 0 + 4 + + + + + 160 + True + False + Right eyebrow Y + + + 0 + 5 + + + + + 160 + True + False + RIght eyebrow angle + + + 0 + 6 + + + + + 160 + True + False + Right eyebrow form + + + 0 + 7 + + + + + Clear + 120 + True + True + 60 + + + 4 + 1 + + + + + Clear + 120 + True + True + 60 + + + 4 + 2 + + + + + Clear + 120 + True + True + 60 + + + 4 + 0 + + + + + Clear + 120 + True + True + 60 + + + 4 + 3 + + + + + Clear + 120 + True + True + 60 + + + 4 + 5 + + + + + Clear + 120 + True + True + 60 + + + 4 + 4 + + + + + Clear + 120 + True + True + 60 + + + 4 + 7 + + + + + Clear + 120 + True + True + 60 + + + 4 + 6 + + + + + + + True + False + Eyebrows + + + + + False + True + 3 + + + + + True + True + + + True + False + + + 160 + True + False + Mouth open + + + 0 + 0 + + + + + 160 + True + False + Mouth form + + + 0 + 1 + + + + + 160 + True + False + Tere (blush) + + + 0 + 2 + + + + + – + 30 + True + False + True + True + + + 1 + 0 + + + + + ☹ + True + True + True + + + 1 + 1 + + + + + – + True + True + True + + + 1 + 2 + + + + + 200 + True + False + True + adjMouthOpenY + 1 + False + + + 2 + 0 + + + + + 200 + True + True + adjMouthForm + 1 + False + + + 2 + 1 + + + + + 200 + True + True + adjTere + 1 + False + + + 2 + 2 + + + + + + + 30 + True + False + True + True + + + 3 + 0 + + + + + ☺ + True + True + True + + + 3 + 1 + + + + + + + True + True + True + + + 3 + 2 + + + + + Auto (lip sync) + True + True + False + 50 + True + True + + + 4 + 0 + + + + + Clear + 120 + True + True + 60 + + + 4 + 2 + + + + + Clear + 120 + True + True + 60 + + + 4 + 1 + + + + + + + True + False + Mouth and face + + + + + False + True + 4 + + + + + True + True + + + True + False + + + 160 + True + False + Hair front + + + 0 + 0 + + + + + 160 + True + False + Hair side + + + 0 + 1 + + + + + 160 + True + False + Hair back + + + 0 + 2 + + + + + 160 + True + False + Hair fluffy + + + 0 + 3 + + + + + – + 30 + True + True + True + + + 1 + 0 + + + + + – + True + True + True + + + 1 + 1 + + + + + – + True + True + True + + + 1 + 2 + + + + + – + True + True + True + + + 1 + 3 + + + + + + + 30 + True + True + True + + + 3 + 0 + + + + + + + True + True + True + + + 3 + 1 + + + + + + + True + True + True + + + 3 + 2 + + + + + + + True + True + True + + + 3 + 3 + + + + + 200 + True + True + adjHairFluffy + 1 + False + + + 2 + 3 + + + + + 200 + True + True + adjHairFront + 1 + False + + + 2 + 0 + + + + + 200 + True + True + adjHairSide + 1 + False + + + 2 + 1 + + + + + 200 + True + True + adjHairBack + 1 + False + + + 2 + 2 + + + + + Clear + 120 + True + True + 60 + + + 4 + 0 + + + + + Clear + 120 + True + True + 60 + + + 4 + 2 + + + + + Clear + 120 + True + True + 60 + + + 4 + 1 + + + + + Clear + 120 + True + True + 60 + + + 4 + 3 + + + + + + + True + False + Hair + + + + + False + True + 5 + + + + + True + True + + + True + False + + + 160 + True + False + Breath + + + 0 + 0 + + + + + 160 + True + False + Body angle X + + + 0 + 1 + + + + + 160 + True + False + Body angle Y + + + 0 + 2 + + + + + 160 + True + False + Body angle Z + + + 0 + 3 + + + + + 160 + True + False + Shoulder Y + + + 0 + 4 + + + + + 160 + True + False + Bust X + + + 0 + 5 + + + + + 160 + True + False + Bust Y + + + 0 + 6 + + + + + 160 + True + False + Base X + + + 0 + 7 + + + + + 160 + True + False + Base Y + + + 0 + 8 + + + + + – + 30 + True + False + True + True + + + 1 + 0 + + + + + ↓ + True + True + True + + + 1 + 2 + + + + + ← + True + True + True + + + 1 + 1 + + + + + – + True + True + True + + + 1 + 4 + + + + + ↶ + True + True + True + + + 1 + 3 + + + + + – + True + True + True + + + 1 + 5 + + + + + – + True + True + True + + + 1 + 6 + + + + + – + True + True + True + + + 1 + 8 + + + + + – + True + True + True + + + 1 + 7 + + + + + + + 30 + True + False + True + True + + + 3 + 0 + + + + + → + True + True + True + + + 3 + 1 + + + + + ↑ + True + True + True + + + 3 + 2 + + + + + + + True + True + True + + + 3 + 4 + + + + + ↷ + True + True + True + + + 3 + 3 + + + + + + + True + True + True + + + 3 + 5 + + + + + + + True + True + True + + + 3 + 6 + + + + + + + True + True + True + + + 3 + 7 + + + + + + + True + True + True + + + 3 + 8 + + + + + Auto breath + True + True + False + 60 + True + True + + + 4 + 0 + + + + + 200 + True + False + True + adjBreath + 1 + False + + + 2 + 0 + + + + + 200 + True + True + adjBodyAngleX + 1 + False + + + 2 + 1 + + + + + 200 + True + True + adjBodyAngleY + 1 + False + + + 2 + 2 + + + + + 200 + True + True + adjBodyAngleZ + 1 + False + + + 2 + 3 + + + + + 200 + True + True + adjShoulderY + 1 + False + + + 2 + 4 + + + + + 200 + True + True + adjBustY + 1 + False + + + 2 + 6 + + + + + 200 + True + True + adjBustX + 1 + False + + + 2 + 5 + + + + + 200 + True + True + adjBaseX + 1 + False + + + 2 + 7 + + + + + 200 + True + True + adjBaseY + 1 + False + + + 2 + 8 + + + + + Clear + 120 + True + True + 60 + + + 4 + 1 + + + + + Clear + 120 + True + True + 60 + + + 4 + 3 + + + + + Clear + 120 + True + True + 60 + + + 4 + 2 + + + + + Clear + 120 + True + True + 60 + + + 4 + 4 + + + + + Clear + 120 + True + True + 60 + + + 4 + 5 + + + + + Clear + 120 + True + True + 60 + + + 4 + 6 + + + + + Clear + 120 + True + True + 60 + + + 4 + 7 + + + + + Clear + 120 + True + True + 60 + + + 4 + 8 + + + + + + + True + False + Body + + + + + False + True + 6 + + + + + True + True + + + True + False + + + 160 + True + False + Left arm A + + + 0 + 0 + + + + + 160 + True + False + Left arm B + + + 0 + 1 + + + + + 160 + True + False + Left hand + + + 0 + 2 + + + + + 160 + True + False + Right arm A + + + 0 + 3 + + + + + 160 + True + False + Right arm B + + + 0 + 4 + + + + + 160 + True + False + Right hand + + + 0 + 5 + + + + + – + 30 + True + True + True + + + 1 + 0 + + + + + – + True + True + True + + + 1 + 1 + + + + + – + True + True + True + + + 1 + 2 + + + + + – + True + True + True + + + 1 + 3 + + + + + – + True + True + True + + + 1 + 4 + + + + + – + True + True + True + + + 1 + 5 + + + + + + + 30 + True + True + True + + + 3 + 0 + + + + + + + True + True + True + + + 3 + 1 + + + + + + + True + True + True + + + 3 + 2 + + + + + + + True + True + True + + + 3 + 3 + + + + + + + True + True + True + + + 3 + 4 + + + + + + + True + True + True + + + 3 + 5 + + + + + 200 + True + True + adjArmLA + 1 + False + + + 2 + 0 + + + + + 200 + True + True + adjArmLB + 1 + False + + + 2 + 1 + + + + + 200 + True + True + adjArmRA + 1 + False + + + 2 + 3 + + + + + 200 + True + True + adjArmRB + 1 + False + + + 2 + 4 + + + + + 200 + True + True + adjHandL + 1 + False + + + 2 + 2 + + + + + 200 + True + True + adjHandR + 1 + False + + + 2 + 5 + + + + + Clear + 120 + True + True + 60 + + + 4 + 0 + + + + + Clear + 120 + True + True + 60 + + + 4 + 1 + + + + + Clear + 120 + True + True + 60 + + + 4 + 2 + + + + + Clear + 120 + True + True + 60 + + + 4 + 4 + + + + + Clear + 120 + True + True + 60 + + + 4 + 3 + + + + + Clear + 120 + True + True + 60 + + + 4 + 5 + + + + + + + True + False + Arms and hands + + + + + False + True + 7 + + + + + + diff --git a/src/mouse_cursor_tracker.cpp b/src/mouse_cursor_tracker.cpp index 301e875..d991213 100644 --- a/src/mouse_cursor_tracker.cpp +++ b/src/mouse_cursor_tracker.cpp @@ -37,6 +37,8 @@ SOFTWARE. #include #include +#include "mouse_cursor_tracker.h" + extern "C" { #include @@ -44,7 +46,7 @@ extern "C" #include // strdup #include "editline.h" } -#include "mouse_cursor_tracker.h" + static double rms(float *buf, std::size_t count) { @@ -303,8 +305,11 @@ MouseCursorTracker::MouseCursorTracker(std::string cfgPath, throw std::runtime_error("Unable to create pulse"); } + m_gtkapp = Gtk::Application::create(); + m_getVolumeThread = std::thread(&MouseCursorTracker::audioLoop, this); m_parseCommandThread = std::thread(&MouseCursorTracker::cliLoop, this); + m_guiThread = std::thread(&MouseCursorTracker::guiLoop, this); MCT_motions = motions; MCT_expressions = expressions; @@ -364,7 +369,7 @@ void MouseCursorTracker::processCommand(std::string cmdline) { if (cmdSplit.size() == 1) { - std::cout << "Available commands: motion set clear\n" + std::cout << "Available commands: motion expression set clear\n" << "Type \"help \" for more help" << std::endl; } else if (cmdSplit[1] == "motion") diff --git a/src/mouse_cursor_tracker_gui.cpp b/src/mouse_cursor_tracker_gui.cpp new file mode 100644 index 0000000..2a45157 --- /dev/null +++ b/src/mouse_cursor_tracker_gui.cpp @@ -0,0 +1,378 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mouse_cursor_tracker.h" + +extern std::vector live2dParams; +extern std::vector > MCT_motions; +extern std::vector MCT_expressions; +extern std::map *MCT_overrideMap; + +#define GET_WIDGET_ASSERT(id, widgetPtr) do { \ + m_builder->get_widget(id, widgetPtr); \ + if (!widgetPtr) abort(); \ + } while (0) + +void MouseCursorTracker::onMotionStartButton(void) +{ + std::unique_lock lock(m_motionMutex, std::defer_lock); + lock.lock(); + + Gtk::ComboBoxText *cbt; + GET_WIDGET_ASSERT("comboBoxMotions", cbt); + std::string motionStr = cbt->get_active_text(); + if (!motionStr.empty()) + { + std::stringstream ss(motionStr); + ss >> m_motionGroup >> m_motionNumber; + } + + GET_WIDGET_ASSERT("comboBoxMotionPriority", cbt); + std::string priority = cbt->get_active_id(); + if (priority == "priorityForce") + { + m_motionPriority = MotionPriority::force; + } + else if (priority == "priorityNormal") + { + m_motionPriority = MotionPriority::normal; + } + else if (priority == "priorityIdle") + { + m_motionPriority = MotionPriority::idle; + } + else + { + m_motionPriority = MotionPriority::none; + } + + lock.unlock(); +} + +void MouseCursorTracker::onExpressionStartButton(void) +{ + std::unique_lock lock(m_motionMutex, std::defer_lock); + lock.lock(); + + Gtk::ComboBoxText *cbt; + GET_WIDGET_ASSERT("comboBoxExpressions", cbt); + std::string expStr = cbt->get_active_text(); + if (!expStr.empty()) + { + m_expression = expStr; + } + + lock.unlock(); +} + +void MouseCursorTracker::onParamUpdateButton(Gtk::Scale *scale, bool isInc) +{ + Glib::RefPtr adj = scale->get_adjustment(); + double value = adj->get_value(); + double increment = adj->get_step_increment(); + adj->set_value(value + increment * (isInc ? 1 : -1)); +} + +void MouseCursorTracker::onParamValChanged(Glib::RefPtr adj, std::string paramName) +{ + bool isResetting = std::find( + m_onClearResettingParams.begin(), + m_onClearResettingParams.end(), + paramName) != m_onClearResettingParams.end(); + + if (isResetting) + { + // Value changed event caused by onClearButton, + // don't change underlying value + return; + } + + double value = adj->get_value(); + m_overrideMap["Param" + paramName] = value; + /* Use get_object instead of get_widget to avoid + * "widget not found" error messages */ + auto obj = m_builder->get_object("button" + paramName + "Clear"); + if (obj) + { + auto button = Glib::RefPtr::cast_dynamic(obj); + button->set_visible(true); + } +} + +void MouseCursorTracker::onClearButton(Glib::RefPtr button, std::string paramName, Glib::RefPtr adj) +{ + m_overrideMap.erase("Param" + paramName); + + // Reset the GtkScale display value to 0, without running the callback. + m_onClearResettingParams.push_back(paramName); + if (adj) + { + double value = 0; + // Special case for MouthForm: use value from config file + if (paramName == "MouthForm") + { + value = m_cfg.mouthForm; + } + adj->set_value(value); + } + m_onClearResettingParams.erase( + std::remove( + m_onClearResettingParams.begin(), + m_onClearResettingParams.end(), + paramName), + m_onClearResettingParams.end() + ); + + button->set_visible(false); +} + +void MouseCursorTracker::onAutoToggle(Gtk::CheckButton *check, Gtk::Scale *scale, + Gtk::Button *buttonDec, Gtk::Button *buttonInc, + std::string paramName) +{ + bool active = check->get_active(); + scale->set_sensitive(!active); + buttonDec->set_sensitive(!active); + buttonInc->set_sensitive(!active); + + if (active) + { + m_overrideMap.erase(paramName); + } + else + { + auto adj = scale->get_adjustment(); + double value = adj->get_value(); + m_overrideMap[paramName] = value; + } + + // Special cases for lip sync and auto breath flags + if (paramName == "ParamMouthOpenY") + { + m_cfg.useLipSync = active; + } + else if (paramName == "ParamBreath") + { + m_cfg.autoBreath = active; + } +} + +void MouseCursorTracker::onExpanderChange(Gtk::Window *window) +{ + // Shrink window if enlarged by GtkExpander + window->resize(1, 1); +} + +void MouseCursorTracker::guiLoop(void) +{ + m_builder = Gtk::Builder::create_from_file("gui.glade"); + + // Add motions list to combobox + Gtk::ComboBoxText *motionsCbt; + GET_WIDGET_ASSERT("comboBoxMotions", motionsCbt); + for (auto it = MCT_motions.begin(); it != MCT_motions.end(); ++it) + { + for (int i = 0; i < it->second; i++) + { + std::stringstream ss; + ss << it->first << " " << i; + motionsCbt->append(ss.str()); + } + } + + // Add expressions list to combobox + Gtk::ComboBoxText *expsCbt; + GET_WIDGET_ASSERT("comboBoxExpressions", expsCbt); + for (auto it = MCT_expressions.begin(); it != MCT_expressions.end(); ++it) + { + expsCbt->append(*it); + } + + + // Add scale tick marks + Gtk::Scale *scale; + + std::vector ticksAtZero = + { + "scaleAngleX", "scaleAngleY", "scaleAngleZ", + "scaleEyeBallX", "scaleEyeBallY", "scaleEyeBallForm", + "scaleMouthForm", + "scaleBrowLX", "scaleBrowLY", "scaleBrowLAngle", + "scaleBrowLForm", + "scaleBrowRX", "scaleBrowRY", "scaleBrowRAngle", + "scaleBrowRForm", + "scaleHairFront", "scaleHairSide", "scaleHairBack", + "scaleBodyAngleX", "scaleBodyAngleY", "scaleBodyAngleZ", + "scaleBustX", "scaleBustY", "scaleBaseX", "scaleBaseY", + "scaleArmLA", "scaleArmLB", "scaleArmRA", "scaleArmRB", + "scaleHandL", "scaleHandR" + }; + + for (auto it = ticksAtZero.begin(); it != ticksAtZero.end(); ++it) + { + GET_WIDGET_ASSERT(*it, scale); + scale->add_mark(0, Gtk::PositionType::POS_BOTTOM, ""); + } + + GET_WIDGET_ASSERT("scaleEyeLOpen", scale); + scale->add_mark(0, Gtk::PositionType::POS_BOTTOM, ""); + scale->add_mark(1, Gtk::PositionType::POS_BOTTOM, ""); + GET_WIDGET_ASSERT("scaleEyeROpen", scale); + scale->add_mark(0, Gtk::PositionType::POS_BOTTOM, ""); + scale->add_mark(1, Gtk::PositionType::POS_BOTTOM, ""); + + GET_WIDGET_ASSERT("scaleMouthOpenY", scale); + scale->add_mark(1, Gtk::PositionType::POS_BOTTOM, ""); + + // Bind button handlers + Gtk::Button *button; + GET_WIDGET_ASSERT("buttonStartMotion", button); + button->signal_clicked().connect(sigc::mem_fun(*this, &MouseCursorTracker::onMotionStartButton)); + + GET_WIDGET_ASSERT("buttonStartExpression", button); + button->signal_clicked().connect(sigc::mem_fun(*this, &MouseCursorTracker::onExpressionStartButton)); + + // Bind button handlers for increment / decrement buttons + for (auto it = live2dParams.begin(); it != live2dParams.end(); ++it) + { + std::string paramName = *it; // e.g. ParamAngleX + paramName.erase(0, 5); // e.g. AngleX + std::string buttonDecId = "button" + paramName + "Dec"; + std::string buttonIncId = "button" + paramName + "Inc"; + std::string buttonClrId = "button" + paramName + "Clear"; + std::string scaleId = "scale" + paramName; + + m_builder->get_widget(scaleId, scale); + + m_builder->get_widget(buttonDecId, button); + if (button && scale) + { + button->signal_clicked().connect( + sigc::bind( + sigc::mem_fun(*this, &MouseCursorTracker::onParamUpdateButton), + scale, false + ) + ); + } + + m_builder->get_widget(buttonIncId, button); + if (button && scale) + { + button->signal_clicked().connect( + sigc::bind( + sigc::mem_fun(*this, &MouseCursorTracker::onParamUpdateButton), + scale, true + ) + ); + } + + Glib::RefPtr adj; + if (scale) + { + adj = scale->get_adjustment(); + if (!adj) abort(); + adj->signal_value_changed().connect( + sigc::bind, std::string>( + sigc::mem_fun(*this, &MouseCursorTracker::onParamValChanged), + adj, paramName + ) + ); + } + + /* Use get_object instead of get_widget to avoid + * "widget not found" error messages */ + auto obj = m_builder->get_object(buttonClrId); + if (obj) + { + auto buttonClr = Glib::RefPtr::cast_dynamic(obj); + buttonClr->signal_clicked().connect( + sigc::bind, std::string, Glib::RefPtr >( + sigc::mem_fun(*this, &MouseCursorTracker::onClearButton), + buttonClr, paramName, adj + ) + ); + } + } + + // Bind handlers for auto params check boxes + Gtk::CheckButton *check; + Gtk::Button *buttonInc; + Gtk::Button *buttonDec; + + std::vector autoTracked = + { + "AngleX", "AngleY", "EyeLOpen", "EyeROpen", "MouthOpenY", "Breath" + }; + + for (auto it = autoTracked.begin(); it != autoTracked.end(); ++it) + { + GET_WIDGET_ASSERT("check" + *it, check); + GET_WIDGET_ASSERT("scale" + *it, scale); + GET_WIDGET_ASSERT("button" + *it + "Inc", buttonInc); + GET_WIDGET_ASSERT("button" + *it + "Dec", buttonDec); + check->signal_toggled().connect( + sigc::bind( + sigc::mem_fun(*this, &MouseCursorTracker::onAutoToggle), + check, scale, buttonDec, buttonInc, "Param" + *it + ) + ); + } + + // Set some values from config file + GET_WIDGET_ASSERT("checkMouthOpenY", check); + check->set_active(m_cfg.useLipSync); + GET_WIDGET_ASSERT("checkBreath", check); + check->set_active(m_cfg.autoBreath); + GET_WIDGET_ASSERT("scaleMouthForm", scale); + auto adj = scale->get_adjustment(); + // Don't trigger value changed event + m_onClearResettingParams.push_back("MouthForm"); + adj->set_value(m_cfg.mouthForm); + m_onClearResettingParams.erase( + std::remove( + m_onClearResettingParams.begin(), + m_onClearResettingParams.end(), + "MouthForm"), + m_onClearResettingParams.end() + ); + + Gtk::Window *window; + GET_WIDGET_ASSERT("windowMain", window); + + std::vector expanders = + { + "expanderHead", "expanderEyes", "expanderEyebrows", + "expanderMouthFace", "expanderHair", "expanderBody", + "expanderArmsHands" + }; + Gtk::Expander *expander; + for (auto it = expanders.begin(); it != expanders.end(); ++it) + { + m_builder->get_widget(*it, expander); + if (expander) + { + expander->property_expanded().signal_changed().connect( + sigc::bind( + sigc::mem_fun(*this, &MouseCursorTracker::onExpanderChange), + window + ) + ); + } + } + + m_gtkapp->run(*window); +} + -- 2.7.4