diff --git a/ControlManager.cpp b/ControlManager.cpp index 03231bc..01b3325 100644 --- a/ControlManager.cpp +++ b/ControlManager.cpp @@ -50,7 +50,6 @@ bool Control::input_active[INPUT_MAX]{}; float Control::input_values[INPUT_MAX]{}; -std::condition_variable Control::joystick_end_; void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m, @@ -98,6 +97,11 @@ void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m, Control::manager().sendOutputStatus(remoteEndpoint); } } + // Multitouch target: user input on 'Multitouch' tab + else if ( target.compare(OSC_MULTITOUCH) == 0 ) + { + Control::manager().receiveMultitouchAttribute(attribute, m.ArgumentStream()); + } // Session target: concerns attributes of the session else if ( target.compare(OSC_SESSION) == 0 ) { @@ -248,7 +252,10 @@ std::string Control::RequestListener::FullMessage( const osc::ReceivedMessage& m Control::Control() : receiver_(nullptr) { - + for (size_t i = 0; i < INPUT_MULTITOUCH_COUNT; ++i) { + multitouch_active[i] = false; + multitouch_values[i] = glm::vec2(0.f); + } } Control::~Control() @@ -354,12 +361,6 @@ bool Control::init() glfwSetKeyCallback( main, Control::keyboardCalback); glfwSetKeyCallback( output, Control::keyboardCalback); - - // - // set joystick callback - // - std::thread(Control::joystickCallback).detach(); - // // load OSC Translator // @@ -393,6 +394,38 @@ bool Control::init() return receiver_ != nullptr; } +void Control::update() +{ + // read joystick buttons + int num_buttons = 0; + const unsigned char *state_buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &num_buttons ); + // map to Control input array + for (int b = 0; b < num_buttons; ++b) { + Control::input_active[INPUT_JOYSTICK_FIRST_BUTTON + b] = state_buttons[b] == GLFW_PRESS; + Control::input_values[INPUT_JOYSTICK_FIRST_BUTTON + b] = state_buttons[b] == GLFW_PRESS ? 1.f : 0.f; + } + + // read joystick axis + int num_axis = 0; + const float *state_axis = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &num_axis ); + for (int a = 0; a < num_axis; ++a) { + Control::input_active[INPUT_JOYSTICK_FIRST_AXIS + a] = ABS(state_axis[a]) > 0.02 ? true : false; + Control::input_values[INPUT_JOYSTICK_FIRST_AXIS + a] = state_axis[a]; + } + + // multitouch input needs to be cleared when no more OSC input comes in + for (int m = 0; m < INPUT_MULTITOUCH_COUNT; ++m) { + if ( multitouch_active[m] > 0 ) + multitouch_active[m] -= 1; + else { + Control::input_active[INPUT_MULTITOUCH_FIRST + m] = false; + Control::input_values[INPUT_MULTITOUCH_FIRST + m] = 0.f; + multitouch_values[m] = glm::vec2(0.f); + } + } + +} + void Control::listen() { if (Control::manager().receiver_) @@ -403,8 +436,6 @@ void Control::listen() void Control::terminate() { - Control::joystick_end_.notify_all(); - if ( receiver_ != nullptr ) { // request termination of receiver @@ -610,8 +641,6 @@ bool Control::receiveSourceAttribute(Source *target, const std::string &attribut return send_feedback; } - - bool Control::receiveSessionAttribute(const std::string &attribute, osc::ReceivedMessageArgumentStream arguments) { @@ -654,6 +683,49 @@ bool Control::receiveSessionAttribute(const std::string &attribute, return send_feedback; } +void Control::receiveMultitouchAttribute(const std::string &attribute, + osc::ReceivedMessageArgumentStream arguments) +{ + try { + // address should be in the form /vimix/multitouch/i + int t = -1; + if ( BaseToolkit::is_a_number(attribute.substr(1), &t) && t >= 0 && t < INPUT_MULTITOUCH_COUNT ) + { + // get value inputs + float x = 0.f, y = 0.f; + if ( !arguments.Eos()) + arguments >> x >> y >> osc::EndMessage; + + // if the touch was already pressed + if ( multitouch_active[t] > 0 ) { + // active value decreases with the distance from original press position + Control::input_values[INPUT_MULTITOUCH_FIRST + t] = 1.f - glm::distance(Control::multitouch_values[t], glm::vec2(x, y)) / M_SQRT2; + } + // first time touch is pressed + else { + // store original press position + multitouch_values[t] = glm::vec2(x, y); + // active value is 1.f at first press (full) + Control::input_values[INPUT_MULTITOUCH_FIRST + t] = 1.f; + } + // keep track of button press + multitouch_active[t] = 3; + // set array of active input + Control::input_active[INPUT_MULTITOUCH_FIRST + t] = true; + } + } + catch (osc::MissingArgumentException &e) { + Log::Info(CONTROL_OSC_MSG "Missing argument for attribute '%s' for target %s.", attribute.c_str(), OSC_MULTITOUCH); + } + catch (osc::ExcessArgumentException &e) { + Log::Info(CONTROL_OSC_MSG "Too many arguments for attribute '%s' for target %s.", attribute.c_str(), OSC_MULTITOUCH); + } + catch (osc::WrongArgumentTypeException &e) { + Log::Info(CONTROL_OSC_MSG "Invalid argument for attribute '%s' for target %s.", attribute.c_str(), OSC_MULTITOUCH); + } + +} + void Control::sendSourceAttibutes(const IpEndpointName &remoteEndpoint, std::string target, Source *s) { // default values @@ -782,8 +854,6 @@ void Control::sendOutputStatus(const IpEndpointName &remoteEndpoint) socket.Send( p.Data(), p.Size() ); } - - void Control::keyboardCalback(GLFWwindow* window, int key, int, int action, int mods) { if (UserInterface::manager().keyboardAvailable() && !mods ) @@ -805,39 +875,11 @@ void Control::keyboardCalback(GLFWwindow* window, int key, int, int action, int } } -void Control::joystickCallback() -{ - // wait for the joystick_end_ notification - std::mutex mtx; - std::unique_lock lck(mtx); - // loop with a fixed refresh rate (~ 30 Hz) - while ( Control::joystick_end_.wait_for(lck,std::chrono::milliseconds(33) ) == std::cv_status::timeout ) { - - // read joystick buttons - int num_buttons = 0; - const unsigned char *state_buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &num_buttons ); - // map to Control input array - for (int b = 0; b < num_buttons; ++b) { - Control::input_active[INPUT_JOYSTICK_FIRST_BUTTON + b] = state_buttons[b] == GLFW_PRESS; - Control::input_values[INPUT_JOYSTICK_FIRST_BUTTON + b] = state_buttons[b] == GLFW_PRESS ? 1.f : 0.f; - } - - // read joystick axis - int num_axis = 0; - const float *state_axis = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &num_axis ); - for (int a = 0; a < num_axis; ++a) { - Control::input_active[INPUT_JOYSTICK_FIRST_AXIS + a] = ABS(state_axis[a]) > 0.02 ? true : false; - Control::input_values[INPUT_JOYSTICK_FIRST_AXIS + a] = state_axis[a]; - } - } -} - bool Control::inputActive(uint id) { return Control::input_active[MIN(id,INPUT_MAX)] && !Settings::application.mapping.disabled; } - float Control::inputValue(uint id) { return Control::input_values[MIN(id,INPUT_MAX)]; @@ -867,6 +909,10 @@ std::string Control::inputLabel(uint id) "Right Axis X", "Right Axis Y", "Right Trigger" }; label = joystick_labels[id - INPUT_JOYSTICK_FIRST]; } + else if ( id >= INPUT_MULTITOUCH_FIRST && id <= INPUT_MULTITOUCH_LAST ) + { + label = std::string( "Multitouch ") + std::to_string(id - INPUT_MULTITOUCH_FIRST); + } else if ( id >= INPUT_CUSTOM_FIRST && id <= INPUT_CUSTOM_LAST ) { diff --git a/ControlManager.h b/ControlManager.h index 891399d..0a22c3f 100644 --- a/ControlManager.h +++ b/ControlManager.h @@ -47,18 +47,26 @@ #define OSC_SESSION "/session" #define OSC_SESSION_VERSION "/version" +#define OSC_MULTITOUCH "/multitouch" + #define INPUT_UNDEFINED 0 #define INPUT_KEYBOARD_FIRST 1 +#define INPUT_KEYBOARD_COUNT 25 #define INPUT_KEYBOARD_LAST 26 #define INPUT_NUMPAD_FIRST 27 +#define INPUT_NUMPAD_COUNT 16 #define INPUT_NUMPAD_LAST 43 #define INPUT_JOYSTICK_FIRST 44 +#define INPUT_JOYSTICK_COUNT 20 #define INPUT_JOYSTICK_LAST 64 #define INPUT_JOYSTICK_FIRST_BUTTON 44 #define INPUT_JOYSTICK_LAST_BUTTON 58 #define INPUT_JOYSTICK_FIRST_AXIS 59 #define INPUT_JOYSTICK_LAST_AXIS 64 -#define INPUT_CUSTOM_FIRST 65 +#define INPUT_MULTITOUCH_FIRST 65 +#define INPUT_MULTITOUCH_COUNT 16 +#define INPUT_MULTITOUCH_LAST 81 +#define INPUT_CUSTOM_FIRST 82 #define INPUT_CUSTOM_LAST 99 #define INPUT_MAX 100 @@ -86,6 +94,7 @@ public: ~Control(); bool init(); + void update(); void terminate(); // OSC translation @@ -111,6 +120,8 @@ protected: osc::ReceivedMessageArgumentStream arguments); bool receiveSessionAttribute(const std::string &attribute, osc::ReceivedMessageArgumentStream arguments); + void receiveMultitouchAttribute(const std::string &attribute, + osc::ReceivedMessageArgumentStream arguments); void sendSourceAttibutes(const IpEndpointName& remoteEndpoint, std::string target, Source *s = nullptr); void sendSourcesStatus(const IpEndpointName& remoteEndpoint, @@ -130,11 +141,11 @@ private: static bool input_active[INPUT_MAX]; static float input_values[INPUT_MAX]; - static std::condition_variable joystick_end_; - static std::mutex access_; + int multitouch_active[INPUT_MULTITOUCH_COUNT]; + glm::vec2 multitouch_values[INPUT_MULTITOUCH_COUNT]; static void keyboardCalback(GLFWwindow*, int, int, int, int); - static void joystickCallback(); + }; #endif // CONTROL_H diff --git a/Settings.cpp b/Settings.cpp index 82237d2..8ad7414 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -267,9 +267,9 @@ void Settings::Save(uint64_t runtime) // Inputs mapping XMLElement *mappingConfNode = xmlDoc.NewElement( "Mapping" ); - timerConfNode->SetAttribute("mode", application.mapping.mode); - timerConfNode->SetAttribute("current", application.mapping.current); - timerConfNode->SetAttribute("disabled", application.mapping.disabled); + mappingConfNode->SetAttribute("mode", application.mapping.mode); + mappingConfNode->SetAttribute("current", application.mapping.current); + mappingConfNode->SetAttribute("disabled", application.mapping.disabled); pRoot->InsertEndChild(mappingConfNode); // Controller diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 83a7663..355996a 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -4289,8 +4289,11 @@ void TimerMetronome::Render() InputMappingInterface::InputMappingInterface() : WorkspaceWindow("InputMappingInterface") { - input_mode = { ICON_FA_KEYBOARD " Keyboard", ICON_FA_CALCULATOR " Numpad" , ICON_FA_GAMEPAD " Gamepad" }; - current_input_for_mode = { INPUT_KEYBOARD_FIRST, INPUT_NUMPAD_FIRST, INPUT_JOYSTICK_FIRST }; + input_mode = { ICON_FA_KEYBOARD " Keyboard", + ICON_FA_CALCULATOR " Numpad" , + ICON_FA_TABLET_ALT " TouchOSC" , + ICON_FA_GAMEPAD " Gamepad" }; + current_input_for_mode = { INPUT_KEYBOARD_FIRST, INPUT_NUMPAD_FIRST, INPUT_MULTITOUCH_FIRST, INPUT_JOYSTICK_FIRST }; current_input_ = current_input_for_mode[Settings::application.mapping.mode]; } @@ -4536,7 +4539,6 @@ void InputMappingInterface::Render() ImVec2 frame_top = ImGui::GetCursorScreenPos(); // create data structures more adapted for display - static uint copy_input_callback = INPUT_UNDEFINED; std::multimap< uint, std::pair > input_sources_callbacks; bool input_assigned[INPUT_MAX]{}; // loop over sources of the session @@ -4577,7 +4579,7 @@ void InputMappingInterface::Render() current_input_ = ik; } // draw key button - ImGui::PushID(i); + ImGui::PushID(ik); if (ImGui::Selectable(Control::manager().inputLabel(ik).c_str(), input_assigned[ik], 0, keyLetterIconSize)) { current_input_ = ik; } @@ -4653,7 +4655,7 @@ void InputMappingInterface::Render() current_input_ = ik; } // draw key button - ImGui::PushID(p); + ImGui::PushID(ik); if (ImGui::Selectable(Control::manager().inputLabel(ik).c_str(), input_assigned[ik], 0, iconsize)) { current_input_ = ik; } @@ -4697,10 +4699,67 @@ void InputMappingInterface::Render() } // - // JOYSTICK + // MULTITOUCH OSC // else if ( Settings::application.mapping.mode == 2 ) { + // Draw table of TouchOSC buttons + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.50f)); + ImVec4 color = ImGui::GetStyle().Colors[ImGuiCol_Header]; + color.w /= Settings::application.mapping.disabled ? 2.f : 0.9f; + ImGui::PushStyleColor(ImGuiCol_Header, color); + color = ImGui::GetStyle().Colors[ImGuiCol_Text]; + color.w /= Settings::application.mapping.disabled ? 2.f : 1.0f; + ImGui::PushStyleColor(ImGuiCol_Text, color); + + const ImVec2 touch_bar_size = keyNumpadItemSize * ImVec2(0.65f, 0.2f); + const ImVec2 touch_bar_pos = keyNumpadItemSize * ImVec2(0.1f, 0.6f); + + for (size_t t = 0; t < INPUT_MULTITOUCH_COUNT; ++t){ + uint it = INPUT_MULTITOUCH_FIRST + t; + ImVec2 pos = frame_top + keyNumpadItemSize * ImVec2( t % 4, t / 4); + + // draw overlay on active keys + if ( Control::inputActive(it) ) { + draw_list->AddRectFilled(pos, pos + keyNumpadIconSize, ImGui::GetColorU32(ImGuiCol_Border), 6.f); + // set current + current_input_ = it; + } + + // draw key button + ImGui::PushID(it); + if (ImGui::Selectable(" ", input_assigned[it], 0, keyNumpadIconSize)) + current_input_ = it; + ImGui::PopID(); + + // 4 elements in a row + if ((t % 4) < 3) ImGui::SameLine(); + + // Draw frame + if (it == current_input_) + draw_list->AddRect(pos, pos + keyNumpadIconSize, ImGui::GetColorU32(ImGuiCol_Text), 6.f, ImDrawCornerFlags_All, 3.f); + else + draw_list->AddRect(pos, pos + keyNumpadIconSize, ImGui::GetColorU32(ImGuiCol_TextDisabled), 6.f, ImDrawCornerFlags_All, 0.2f); + + // Draw value bar + ImVec2 prev = ImGui::GetCursorScreenPos(); + ImGui::SetCursorScreenPos( pos + touch_bar_pos); + ImGui::ProgressBar(Control::inputValue(it), touch_bar_size, ""); + ImGui::SetCursorScreenPos( prev ); + + } + + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(); + ImGui::PopFont(); + + } + // + // JOYSTICK + // + else if ( Settings::application.mapping.mode == 3 ) { + // custom layout of gamepad buttons std::vector gamepad_inputs = { INPUT_JOYSTICK_FIRST_BUTTON+11, INPUT_JOYSTICK_FIRST_BUTTON+13, INPUT_JOYSTICK_FIRST_BUTTON+6, @@ -4714,9 +4773,9 @@ void InputMappingInterface::Render() INPUT_JOYSTICK_FIRST_BUTTON+8, INPUT_JOYSTICK_FIRST_BUTTON+10, INPUT_JOYSTICK_FIRST_BUTTON+5 }; - std::vector< std::string > gamepad_labels = { ICON_FA_CARET_SQUARE_UP, ICON_FA_CARET_SQUARE_DOWN, + std::vector< std::string > gamepad_labels = { ICON_FA_ARROW_UP, ICON_FA_ARROW_DOWN, ICON_FA_CHEVRON_CIRCLE_LEFT, "X", "Y", - ICON_FA_CARET_SQUARE_LEFT, ICON_FA_CARET_SQUARE_RIGHT, + ICON_FA_ARROW_LEFT, ICON_FA_ARROW_RIGHT, ICON_FA_CHEVRON_CIRCLE_RIGHT, "A", "B", "L1", "LT", ICON_FA_DOT_CIRCLE, "RT", "R1" }; @@ -5927,12 +5986,6 @@ void Navigator::RenderNewPannel() s->replay(); // close NEW pannel togglePannelNew(); - - /// BHBN TEST SOURCE CALLBACKS KEYaa -// s->setKeyCallback(GLFW_KEY_A, new GotoAlpha(0.9)); -//// s->setKeyCallback(GLFW_KEY_A, new GotoDepth(10.0)); -// s->setKeyCallback(GLFW_KEY_L, new Loom(-0.1)); - } } diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index f6ac7ce..68f6014 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -366,8 +366,8 @@ public: class InputMappingInterface : public WorkspaceWindow { - std::array< std::string, 3 > input_mode; - std::array< uint, 3 > current_input_for_mode; + std::array< std::string, 4 > input_mode; + std::array< uint, 4 > current_input_for_mode; uint current_input_; diff --git a/main.cpp b/main.cpp index 01b4d8e..51e9adf 100644 --- a/main.cpp +++ b/main.cpp @@ -42,6 +42,7 @@ extern "C"{ void prepare() { + Control::manager().update(); Mixer::manager().update(); UserInterface::manager().NewFrame(); } diff --git a/rsc/osc/vimix.mk1.touchosc b/rsc/osc/vimix.mk1.touchosc index cdcc752..b5ad3a2 100644 Binary files a/rsc/osc/vimix.mk1.touchosc and b/rsc/osc/vimix.mk1.touchosc differ