From 7787ad505ac4a87ce97971d50600fc5a7fc88ebd Mon Sep 17 00:00:00 2001 From: rsjaffe Date: Sun, 10 Jul 2016 18:48:55 -0700 Subject: [PATCH 1/8] moving to standard containers --- Source/MIDIProcessor.cpp | 8 +++++--- Source/MIDIProcessor.h | 4 ++-- Source/MIDISender.cpp | 10 +++++----- Source/MIDISender.h | 4 ++-- Source/MainComponent.cpp | 4 ++-- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Source/MIDIProcessor.cpp b/Source/MIDIProcessor.cpp index 5b0546159..9de99a61a 100644 --- a/Source/MIDIProcessor.cpp +++ b/Source/MIDIProcessor.cpp @@ -63,17 +63,19 @@ void MIDIProcessor::addMIDICommandListener(MIDICommandListener* listener) { listeners_.push_back(listener); } -void MIDIProcessor::rescanDevices() { +void MIDIProcessor::RescanDevices() { for (auto const& dev : devices_) dev->stop(); - devices_.clear(true); + devices_.clear(); InitDevices_(); } void MIDIProcessor::InitDevices_() { for (auto idx = 0; idx < MidiInput::getDevices().size(); idx++) { - if (devices_.set(idx, MidiInput::openDevice(idx, this))) { + auto dev = MidiInput::openDevice(idx, this); + if (dev != nullptr) { + devices_.emplace_back(dev); devices_[idx]->start(); DBG(devices_[idx]->getName()); } diff --git a/Source/MIDIProcessor.h b/Source/MIDIProcessor.h index f2ea9883a..9f6a0c5e8 100644 --- a/Source/MIDIProcessor.h +++ b/Source/MIDIProcessor.h @@ -43,7 +43,7 @@ class MIDIProcessor final: private MidiInputCallback { void addMIDICommandListener(MIDICommandListener*); // re-enumerates MIDI IN devices - void rescanDevices(); + void RescanDevices(); private: // overridden from MidiInputCallback @@ -52,7 +52,7 @@ class MIDIProcessor final: private MidiInputCallback { void InitDevices_(); NRPN_Filter nrpn_filter_; - OwnedArray devices_; + std::vector> devices_; std::vector listeners_; }; diff --git a/Source/MIDISender.cpp b/Source/MIDISender.cpp index 33e15d75e..4eb681df5 100644 --- a/Source/MIDISender.cpp +++ b/Source/MIDISender.cpp @@ -30,7 +30,7 @@ void MIDISender::Init(void) { void MIDISender::sendCC(int midi_channel, int controller, int value) const { if (controller < 128) { // regular message - for (auto dev : output_devices) + for (auto& dev : output_devices_) dev->sendMessageNow(MidiMessage::controllerEvent(midi_channel, controller, value)); } @@ -39,7 +39,7 @@ void MIDISender::sendCC(int midi_channel, int controller, int value) const { const auto parameterMSB = (controller >> 7) & 0x7F; const auto valueLSB = value & 0x7f; const auto valueMSB = (value >> 7) & 0x7F; - for (auto dev : output_devices) { + for (auto& dev : output_devices_) { dev->sendMessageNow(MidiMessage::controllerEvent(midi_channel, 99, parameterMSB)); dev->sendMessageNow(MidiMessage::controllerEvent(midi_channel, 98, parameterLSB)); dev->sendMessageNow(MidiMessage::controllerEvent(midi_channel, 6, valueMSB)); @@ -48,8 +48,8 @@ void MIDISender::sendCC(int midi_channel, int controller, int value) const { } } -void MIDISender::rescanDevices() { - output_devices.clear(true); +void MIDISender::RescanDevices() { + output_devices_.clear(); InitDevices_(); } @@ -57,6 +57,6 @@ void MIDISender::InitDevices_() { for (auto idx = 0; idx < MidiOutput::getDevices().size(); idx++) { auto dev = MidiOutput::openDevice(idx); if (dev != nullptr) - output_devices.set(idx, dev); + output_devices_.emplace_back(dev); } } \ No newline at end of file diff --git a/Source/MIDISender.h b/Source/MIDISender.h index 801449a36..8de9efe63 100644 --- a/Source/MIDISender.h +++ b/Source/MIDISender.h @@ -34,11 +34,11 @@ class MIDISender { void sendCC(int midi_channel, int controller, int value) const; // re-enumerates MIDI OUT devices - void rescanDevices(); + void RescanDevices(); private: void InitDevices_(); - OwnedArray output_devices; + std::vector> output_devices_; }; #endif // MIDISENDER_H_INCLUDED diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index dca562d1a..c52364c90 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -218,11 +218,11 @@ void MainContentComponent::buttonClicked(Button* button) { // Re-enumerate MIDI IN and OUT devices if (midi_processor_) { - midi_processor_->rescanDevices(); + midi_processor_->RescanDevices(); } if (midi_sender_) { - midi_sender_->rescanDevices(); + midi_sender_->RescanDevices(); } // Send new CC parameters to MIDI Out devices if (auto ptr = lr_ipc_in_.lock()) { From 49dd35498c4197618599078083768e6993b9ac76 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sun, 10 Jul 2016 19:48:15 -0700 Subject: [PATCH 2/8] get correct limit --- Source/CommandMenu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/CommandMenu.h b/Source/CommandMenu.h index 22814fd2c..8e0ae1290 100644 --- a/Source/CommandMenu.h +++ b/Source/CommandMenu.h @@ -43,7 +43,7 @@ class CommandMenu final: public TextButton, const std::vector menus_; const std::vector> menu_entries_; MIDI_Message message_; - size_t selected_item_{std::numeric_limits::max()}; + size_t selected_item_{std::numeric_limits::max()}; std::shared_ptr command_map_{nullptr}; }; From d2889095df5e589fdbdc9d7d866754e727206a05 Mon Sep 17 00:00:00 2001 From: Rory Jaffe Date: Sun, 10 Jul 2016 19:49:09 -0700 Subject: [PATCH 3/8] std container in profilemanager, added const to weak_ptr locks --- Source/MainComponent.cpp | 8 ++++---- Source/ProfileManager.cpp | 15 ++++++++------- Source/ProfileManager.h | 4 ++-- Source/SettingsComponent.cpp | 6 +++--- Source/SettingsManager.cpp | 8 ++++---- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index c52364c90..02d038086 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -54,7 +54,7 @@ void MainContentComponent::Init(std::shared_ptr& command_map, midi_processor->addMIDICommandListener(this); } - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { // Add ourselves as a listener for LR_IPC_OUT events ptr->addListener(this); } @@ -225,7 +225,7 @@ void MainContentComponent::buttonClicked(Button* button) { midi_sender_->RescanDevices(); } // Send new CC parameters to MIDI Out devices - if (auto ptr = lr_ipc_in_.lock()) { + if (const auto ptr = lr_ipc_in_.lock()) { ptr->refreshMIDIOutput(); } } @@ -288,7 +288,7 @@ void MainContentComponent::buttonClicked(Button* button) { const auto new_profile = browser.getSelectedFile(0); const auto command = String{"ChangedToFullPath "} +new_profile.getFullPathName() + "\n"; - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { ptr->sendCommand(command); } profile_name_label_.setText(new_profile.getFileName(), @@ -322,7 +322,7 @@ void MainContentComponent::profileChanged(XmlElement* xml_element, const String& // _systemTrayComponent.showInfoBubble(filename, "Profile loaded"); // Send new CC parameters to MIDI Out devices - if (auto ptr = lr_ipc_in_.lock()) { + if (const auto ptr = lr_ipc_in_.lock()) { ptr->refreshMIDIOutput(); } } diff --git a/Source/ProfileManager.cpp b/Source/ProfileManager.cpp index 350a05f49..4ef746a8d 100644 --- a/Source/ProfileManager.cpp +++ b/Source/ProfileManager.cpp @@ -30,7 +30,7 @@ void ProfileManager::Init(std::weak_ptr&& out, command_map_ = commandMap; lr_ipc_out_ = std::move(out); - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { // add ourselves as a listener to LR_IPC_OUT so that we can send plugin // settings on connection ptr->addListener(this); @@ -57,18 +57,18 @@ void ProfileManager::setProfileDirectory(const File& directory) { current_profile_index_ = 0; profiles_.clear(); for (const auto file : file_array) - profiles_.add(file.getFileName()); + profiles_.emplace_back(file.getFileName()); if (profiles_.size() > 0) switchToProfile(profiles_[0]); } -const StringArray& ProfileManager::getMenuItems() const noexcept { +const std::vector& ProfileManager::getMenuItems() const noexcept { return profiles_; } void ProfileManager::switchToProfile(int profile_index) { - if (profile_index >= 0 && profile_index < profiles_.size()) { + if (profile_index >= 0 && profile_index < static_cast(profiles_.size())) { switchToProfile(profiles_[profile_index]); current_profile_index_ = profile_index; } @@ -82,7 +82,7 @@ void ProfileManager::switchToProfile(const String& profile) { for (auto listener : listeners_) listener->profileChanged(xml_element.get(), profile); - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { auto command = String{"ChangedToDirectory "} + File::addTrailingSeparator(profile_location_.getFullPathName()) + String{"\n"}; @@ -95,7 +95,8 @@ void ProfileManager::switchToProfile(const String& profile) { void ProfileManager::switchToNextProfile() { current_profile_index_++; - if (current_profile_index_ == profiles_.size()) current_profile_index_ = 0; + if (current_profile_index_ == static_cast(profiles_.size())) + current_profile_index_ = 0; switchToProfile(current_profile_index_); } @@ -150,7 +151,7 @@ void ProfileManager::connected() { const auto command = String{"ChangedToDirectory "} + File::addTrailingSeparator(profile_location_.getFullPathName()) + String{"\n"}; - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { ptr->sendCommand(command); } } diff --git a/Source/ProfileManager.h b/Source/ProfileManager.h index f84824c46..203d96d56 100644 --- a/Source/ProfileManager.h +++ b/Source/ProfileManager.h @@ -50,7 +50,7 @@ class ProfileManager final: public MIDICommandListener, void setProfileDirectory(const File& dir); // returns an array of profile names - const StringArray& getMenuItems() const noexcept; + const std::vector& getMenuItems() const noexcept; // switches to a profile defined by an index void switchToProfile(int profileIdx); @@ -89,7 +89,7 @@ class ProfileManager final: public MIDICommandListener, std::shared_ptr command_map_{nullptr}; std::vector listeners_; std::weak_ptr lr_ipc_out_; - StringArray profiles_; + std::vector profiles_; SWITCH_STATE switch_state_; }; diff --git a/Source/SettingsComponent.cpp b/Source/SettingsComponent.cpp index 9df889b49..0bab5e6de 100644 --- a/Source/SettingsComponent.cpp +++ b/Source/SettingsComponent.cpp @@ -112,7 +112,7 @@ void SettingsComponent::paint(Graphics& g) { void SettingsComponent::buttonClicked(Button* button) { if (button == &pickup_enabled_) { - if (auto ptr = settings_manager_.lock()) + if (const auto ptr = settings_manager_.lock()) ptr->setPickupEnabled(pickup_enabled_.getToggleState()); } else if (button == &profile_location_button_) { @@ -126,7 +126,7 @@ void SettingsComponent::buttonClicked(Button* button) { if (dialog_box.show()) { const auto profile_location = browser.getSelectedFile(0).getFullPathName(); - if (auto ptr = settings_manager_.lock()) { + if (const auto ptr = settings_manager_.lock()) { ptr->setProfileDirectory(profile_location); } profile_location_label_.setText(profile_location, @@ -137,6 +137,6 @@ void SettingsComponent::buttonClicked(Button* button) { void SettingsComponent::sliderValueChanged(Slider* slider) { if (slider && &autohide_setting_ == slider) - if (auto ptr = settings_manager_.lock()) + if (const auto ptr = settings_manager_.lock()) ptr->setAutoHideTime(static_cast(autohide_setting_.getValue())); } \ No newline at end of file diff --git a/Source/SettingsManager.cpp b/Source/SettingsManager.cpp index d489a2b4d..3ffe40c39 100644 --- a/Source/SettingsManager.cpp +++ b/Source/SettingsManager.cpp @@ -38,7 +38,7 @@ void SettingsManager::Init(std::weak_ptr&& lr_ipc_out, std::weak_ptr&& profile_manager) { lr_ipc_out_ = std::move(lr_ipc_out); - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { // add ourselves as a listener to LR_IPC_OUT so that we can send plugin // settings on connection ptr->addListener(this); @@ -60,7 +60,7 @@ void SettingsManager::setPickupEnabled(bool enabled) { properties_file_->setValue("pickup_enabled", enabled); properties_file_->saveIfNeeded(); - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { ptr->sendCommand(String::formatted("Pickup %d\n", enabled)); } } @@ -71,13 +71,13 @@ String SettingsManager::getProfileDirectory() const noexcept { void SettingsManager::setProfileDirectory(const String& profile_directory_name) { properties_file_->setValue("profile_directory", profile_directory_name); properties_file_->saveIfNeeded(); - if (auto ptr = profile_manager_.lock()) { + if (const auto ptr = profile_manager_.lock()) { ptr->setProfileDirectory(profile_directory_name); } } void SettingsManager::connected() { - if (auto ptr = lr_ipc_out_.lock()) { + if (const auto ptr = lr_ipc_out_.lock()) { ptr->sendCommand(String::formatted("Pickup %d\n", getPickupEnabled())); } } From eaac9cd7d7d7410e824ee15db912e0c7d8d566c8 Mon Sep 17 00:00:00 2001 From: rsjaffe Date: Tue, 12 Jul 2016 08:36:20 -0700 Subject: [PATCH 4/8] bugfix: crash when deleting row in app can't delete commands_ row before deleting the corresponding command_map_ row --- Source/CommandTableModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/CommandTableModel.cpp b/Source/CommandTableModel.cpp index ece156364..7f3e4aa9c 100644 --- a/Source/CommandTableModel.cpp +++ b/Source/CommandTableModel.cpp @@ -146,10 +146,10 @@ void CommandTableModel::addRow(int midi_channel, int midi_data, bool is_cc) { } void CommandTableModel::removeRow(int row) { - commands_.erase(commands_.begin() + row); if (command_map_) { command_map_->removeMessage(commands_[row]); } + commands_.erase(commands_.cbegin() + row); } void CommandTableModel::removeAllRows() { From 8e9ce3df170aefe93fb71de4e6432ea06662ae5c Mon Sep 17 00:00:00 2001 From: rsjaffe Date: Tue, 12 Jul 2016 08:36:47 -0700 Subject: [PATCH 5/8] Fix keyboard focus issues Only Foreground LR if MIDI2LR is in foreground --- Source/SendKeys.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Source/SendKeys.cpp b/Source/SendKeys.cpp index b569d17da..23cfb1710 100644 --- a/Source/SendKeys.cpp +++ b/Source/SendKeys.cpp @@ -32,6 +32,11 @@ MIDI2LR. If not, see . #endif namespace { #ifdef _WIN32 + bool IsForegroundProcess() { + return (GetCurrentProcessId() == + GetWindowThreadProcessId(GetForegroundWindow(), NULL)); + } + wchar_t MBtoWChar(const std::string& key) { wchar_t full_character; const auto return_value = MultiByteToWideChar(CP_UTF8, 0, key.data(), @@ -51,18 +56,17 @@ namespace { HKL GetLanguageAndFGApp(std::string program_name) { const auto hLRWnd = FindWindow(NULL, program_name.c_str()); - HKL language_id; - // Bring Lightroom to foreground if it isn't already there if (hLRWnd) { - SetForegroundWindow(hLRWnd); + // Bring Lightroom to foreground if MIDI2LR is in foreground + if (IsForegroundProcess()) + SetForegroundWindow(hLRWnd); // get language that LR is using (if hLrWnd is found) const auto thread_id = GetWindowThreadProcessId(hLRWnd, NULL); - language_id = GetKeyboardLayout(thread_id); + return GetKeyboardLayout(thread_id); } else { // use keyboard of MIDI2LR application - language_id = GetKeyboardLayout(0); + return GetKeyboardLayout(0); } - return language_id; } #else std::wstring utf8_to_utf16(const std::string& utf8) { From 90762a8a8e9ec79ad5c7fd0197c961e3e7870ad6 Mon Sep 17 00:00:00 2001 From: rsjaffe Date: Thu, 14 Jul 2016 15:59:22 -0700 Subject: [PATCH 6/8] code cleanup --- Source/SendKeys.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/SendKeys.cpp b/Source/SendKeys.cpp index 23cfb1710..89fa487cf 100644 --- a/Source/SendKeys.cpp +++ b/Source/SendKeys.cpp @@ -250,7 +250,7 @@ void SendKeys::SendKeyDownUp(const std::string& key, const bool alt_opt, // construct input event. INPUT ip; - constexpr auto size_ip = sizeof(INPUT); + constexpr auto size_ip = sizeof(ip); ip.type = INPUT_KEYBOARD; //ki: wVk, wScan, dwFlags, time, dwExtraInfo ip.ki = {0,0,0,0,0}; From 75672e795ed6489ca8eb06686454b02a40adb2f0 Mon Sep 17 00:00:00 2001 From: rsjaffe Date: Thu, 14 Jul 2016 15:59:59 -0700 Subject: [PATCH 7/8] Observer pattern copied from Mir (Ubuntu) to be adapted to MIDI2LR --- Source/Utilities/basic_observers.h | 33 ++ Source/Utilities/observer.h | 60 ++++ Source/Utilities/surface_observers.h | 55 ++++ Source/Utilities/surface_stack.cpp | 438 +++++++++++++++++++++++++++ Source/Utilities/surface_stack.h | 129 ++++++++ Source/Utilities/thread_safe_list.h | 186 ++++++++++++ 6 files changed, 901 insertions(+) create mode 100644 Source/Utilities/basic_observers.h create mode 100644 Source/Utilities/observer.h create mode 100644 Source/Utilities/surface_observers.h create mode 100644 Source/Utilities/surface_stack.cpp create mode 100644 Source/Utilities/surface_stack.h create mode 100644 Source/Utilities/thread_safe_list.h diff --git a/Source/Utilities/basic_observers.h b/Source/Utilities/basic_observers.h new file mode 100644 index 000000000..a12c40dd2 --- /dev/null +++ b/Source/Utilities/basic_observers.h @@ -0,0 +1,33 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored By: Alan Griffiths + */ + +#ifndef MIR_BASIC_OBSERVERS_H_ +#define MIR_BASIC_OBSERVERS_H_ + +#include "Q:\Documents\GitHub\mir\src\include\common\mir\thread_safe_list.h" +#include + +namespace mir +{ +template +class BasicObservers : protected ThreadSafeList> +{ +}; +} + +#endif /* MIR_BASIC_OBSERVERS_H_ */ diff --git a/Source/Utilities/observer.h b/Source/Utilities/observer.h new file mode 100644 index 000000000..05eaaaaab --- /dev/null +++ b/Source/Utilities/observer.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Robert Carr + */ + +#ifndef MIR_SCENE_OBSERVER_H_ +#define MIR_SCENE_OBSERVER_H_ + +#include + +namespace mir +{ +namespace scene +{ +class Surface; + +/// An observer for top level notifications of scene changes. In order +/// to receive more granular change notifications a user may install +/// mir::scene::SurfaceObserver in surface_added. +class Observer +{ +public: + virtual void surface_added(Surface* surface) = 0; + virtual void surface_removed(Surface* surface) = 0; + virtual void surfaces_reordered() = 0; + + // Used to indicate the scene has changed in some way beyond the present surfaces + // and will require full recomposition. + virtual void scene_changed() = 0; + + // Called at observer registration to notify of already existing surfaces. + virtual void surface_exists(Surface* surface) = 0; + // Called when observer is unregistered, for example, to provide a place to + // unregister SurfaceObservers which may have been added in surface_added/exists + virtual void end_observation() = 0; + +protected: + Observer() = default; + virtual ~Observer() = default; + Observer(Observer const&) = delete; + Observer& operator=(Observer const&) = delete; +}; + +} +} // namespace mir + +#endif // MIR_SCENE_OBSERVER_H_ diff --git a/Source/Utilities/surface_observers.h b/Source/Utilities/surface_observers.h new file mode 100644 index 000000000..589305a30 --- /dev/null +++ b/Source/Utilities/surface_observers.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2015 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Kevin DuBois + */ + +#ifndef MIR_SCENE_SURFACE_OBSERVERS_H_ +#define MIR_SCENE_SURFACE_OBSERVERS_H_ + +#include "mir/basic_observers.h" +#include "mir/scene/surface_observer.h" + +namespace mir +{ +namespace scene +{ + +class SurfaceObservers : public SurfaceObserver, BasicObservers +{ +public: + using BasicObservers::add; + using BasicObservers::remove; + + void attrib_changed(MirSurfaceAttrib attrib, int value) override; + void resized_to(geometry::Size const& size) override; + void moved_to(geometry::Point const& top_left) override; + void hidden_set_to(bool hide) override; + void frame_posted(int frames_available, geometry::Size const& size) override; + void alpha_set_to(float alpha) override; + void orientation_set_to(MirOrientation orientation) override; + void transformation_set_to(glm::mat4 const& t) override; + void reception_mode_set_to(input::InputReceptionMode mode) override; + void cursor_image_set_to(graphics::CursorImage const& image) override; + void client_surface_close_requested() override; + void keymap_changed(xkb_rule_names const& names) override; + void renamed(char const*) override; + void cursor_image_removed() override; +}; + +} +} + +#endif /* MIR_SCENE_SURFACE_OBSERVERS_H_ */ diff --git a/Source/Utilities/surface_stack.cpp b/Source/Utilities/surface_stack.cpp new file mode 100644 index 000000000..a38a14af5 --- /dev/null +++ b/Source/Utilities/surface_stack.cpp @@ -0,0 +1,438 @@ +/* + * Copyright © 2012-2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: + * Alan Griffiths + * Thomas Voss + */ + +#include "surface_stack.h" +#include "rendering_tracker.h" +#include "mir/scene/surface.h" +#include "mir/scene/scene_report.h" +#include "mir/compositor/scene_element.h" +#include "mir/compositor/decoration.h" +#include "mir/graphics/renderable.h" + +#include + +#include +#include +#include +#include +#include + +namespace ms = mir::scene; +namespace mc = mir::compositor; +namespace mg = mir::graphics; +namespace mi = mir::input; +namespace geom = mir::geometry; + +namespace +{ + +class SurfaceSceneElement : public mc::SceneElement +{ +public: + SurfaceSceneElement( + std::string name, + std::shared_ptr const& renderable, + std::shared_ptr const& tracker, + mc::CompositorID id) + : renderable_{renderable}, + tracker{tracker}, + cid{id}, + surface_name(name) + { + } + + std::shared_ptr renderable() const override + { + return renderable_; + } + + void rendered() override + { + tracker->rendered_in(cid); + } + + void occluded() override + { + tracker->occluded_in(cid); + } + + std::unique_ptr decoration() const override + { + return std::make_unique(mc::Decoration::Type::surface, surface_name); + } + +private: + std::shared_ptr const renderable_; + std::shared_ptr const tracker; + mc::CompositorID cid; + std::string const surface_name; +}; + +//note: something different than a 2D/HWC overlay +class OverlaySceneElement : public mc::SceneElement +{ +public: + OverlaySceneElement( + std::shared_ptr renderable) + : renderable_{renderable} + { + } + + std::shared_ptr renderable() const override + { + return renderable_; + } + + void rendered() override + { + } + + void occluded() override + { + } + + std::unique_ptr decoration() const override + { + return std::make_unique(); + } + +private: + std::shared_ptr const renderable_; +}; + +} + +ms::SurfaceStack::SurfaceStack( + std::shared_ptr const& report) : + report{report}, + scene_changed{false} +{ +} + +mc::SceneElementSequence ms::SurfaceStack::scene_elements_for(mc::CompositorID id) +{ + RecursiveReadLock lg(guard); + + scene_changed = false; + mc::SceneElementSequence elements; + for (auto const& surface : surfaces) + { + if (surface->visible()) + { + for (auto& renderable : surface->generate_renderables(id)) + { + elements.emplace_back( + std::make_shared( + surface->name(), + renderable, + rendering_trackers[surface.get()], + id)); + } + } + } + for (auto const& renderable : overlays) + { + elements.emplace_back(std::make_shared(renderable)); + } + return elements; +} + +int ms::SurfaceStack::frames_pending(mc::CompositorID id) const +{ + RecursiveReadLock lg(guard); + + int result = scene_changed ? 1 : 0; + for (auto const& surface : surfaces) + { + if (surface->visible()) + { + auto const tracker = rendering_trackers.find(surface.get()); + if (tracker != rendering_trackers.end() && tracker->second->is_exposed_in(id)) + { + // Note that we ask the surface and not a Renderable. + // This is because we don't want to waste time and resources + // on a snapshot till we're sure we need it... + int ready = surface->buffers_ready_for_compositor(id); + if (ready > result) + result = ready; + } + } + } + return result; +} + +void ms::SurfaceStack::register_compositor(mc::CompositorID cid) +{ + RecursiveWriteLock lg(guard); + + registered_compositors.insert(cid); + + update_rendering_tracker_compositors(); +} + +void ms::SurfaceStack::unregister_compositor(mc::CompositorID cid) +{ + RecursiveWriteLock lg(guard); + + registered_compositors.erase(cid); + + update_rendering_tracker_compositors(); +} + +void ms::SurfaceStack::add_input_visualization( + std::shared_ptr const& overlay) +{ + { + RecursiveWriteLock lg(guard); + overlays.push_back(overlay); + } + emit_scene_changed(); +} + +void ms::SurfaceStack::remove_input_visualization( + std::weak_ptr const& weak_overlay) +{ + auto overlay = weak_overlay.lock(); + { + RecursiveWriteLock lg(guard); + auto const p = std::find(overlays.begin(), overlays.end(), overlay); + if (p == overlays.end()) + { + BOOST_THROW_EXCEPTION(std::runtime_error("Attempt to remove an overlay which was never added or which has been previously removed")); + } + overlays.erase(p); + } + + emit_scene_changed(); +} + +void ms::SurfaceStack::emit_scene_changed() +{ + { + RecursiveWriteLock lg(guard); + scene_changed = true; + } + observers.scene_changed(); +} + +void ms::SurfaceStack::add_surface( + std::shared_ptr const& surface, + mi::InputReceptionMode input_mode) +{ + { + RecursiveWriteLock lg(guard); + surfaces.push_back(surface); + create_rendering_tracker_for(surface); + } + surface->set_reception_mode(input_mode); + observers.surface_added(surface.get()); + + report->surface_added(surface.get(), surface.get()->name()); +} + +void ms::SurfaceStack::remove_surface(std::weak_ptr const& surface) +{ + auto const keep_alive = surface.lock(); + + bool found_surface = false; + { + RecursiveWriteLock lg(guard); + + auto const surface = std::find(surfaces.begin(), surfaces.end(), keep_alive); + + if (surface != surfaces.end()) + { + surfaces.erase(surface); + rendering_trackers.erase(keep_alive.get()); + found_surface = true; + } + } + + if (found_surface) + { + observers.surface_removed(keep_alive.get()); + + report->surface_removed(keep_alive.get(), keep_alive.get()->name()); + } + // TODO: error logging when surface not found +} + +namespace +{ +template +struct InReverse { + Container& container; + auto begin() -> decltype(container.rbegin()) { return container.rbegin(); } + auto end() -> decltype(container.rend()) { return container.rend(); } +}; + +template +InReverse in_reverse(Container& container) { return InReverse{container}; } +} + +auto ms::SurfaceStack::surface_at(geometry::Point cursor) const +-> std::shared_ptr +{ + RecursiveReadLock lg(guard); + for (auto const& surface : in_reverse(surfaces)) + { + // TODO There's a lack of clarity about how the input area will + // TODO be maintained and whether this test will detect clicks on + // TODO decorations (it should) as these may be outside the area + // TODO known to the client. But it works for now. + if (surface->input_area_contains(cursor)) + return surface; + } + + return {}; +} + +void ms::SurfaceStack::for_each(std::function const&)> const& callback) +{ + RecursiveReadLock lg(guard); + for (auto &surface : surfaces) + { + if (surface->query(mir_surface_attrib_visibility) == + MirSurfaceVisibility::mir_surface_visibility_exposed) + { + callback(surface); + } + } +} + +void ms::SurfaceStack::raise(std::weak_ptr const& s) +{ + bool surfaces_reordered{false}; + + { + auto const surface = s.lock(); + + RecursiveWriteLock ul(guard); + auto const p = std::find(surfaces.begin(), surfaces.end(), surface); + + if (p != surfaces.end()) + { + surfaces.erase(p); + surfaces.push_back(surface); + surfaces_reordered = true; + } + } + + if (!surfaces_reordered) + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid surface")); + + observers.surfaces_reordered(); + return; +} + +void ms::SurfaceStack::raise(SurfaceSet const& ss) +{ + bool surfaces_reordered{false}; + { + RecursiveWriteLock ul(guard); + + auto const old_surfaces = surfaces; + std::stable_partition( + begin(surfaces), end(surfaces), + [&](std::weak_ptr const& s) { return !ss.count(s); }); + + if (old_surfaces != surfaces) + surfaces_reordered = true; + } + + if (surfaces_reordered) + observers.surfaces_reordered(); +} + +void ms::SurfaceStack::create_rendering_tracker_for(std::shared_ptr const& surface) +{ + auto const tracker = std::make_shared(surface); + + RecursiveWriteLock ul(guard); + tracker->active_compositors(registered_compositors); + rendering_trackers[surface.get()] = tracker; +} + +void ms::SurfaceStack::update_rendering_tracker_compositors() +{ + RecursiveReadLock ul(guard); + + for (auto const& pair : rendering_trackers) + pair.second->active_compositors(registered_compositors); +} + +void ms::SurfaceStack::add_observer(std::shared_ptr const& observer) +{ + observers.add(observer); + + // Notify observer of existing surfaces + RecursiveReadLock lk(guard); + for (auto &surface : surfaces) + { + observer->surface_exists(surface.get()); + } +} + +void ms::SurfaceStack::remove_observer(std::weak_ptr const& observer) +{ + auto o = observer.lock(); + if (!o) + BOOST_THROW_EXCEPTION(std::logic_error("Invalid observer (destroyed)")); + + o->end_observation(); + + observers.remove(o); +} + +void ms::Observers::surface_added(ms::Surface* surface) +{ + for_each([&](std::shared_ptr const& observer) + { observer->surface_added(surface); }); +} + +void ms::Observers::surface_removed(ms::Surface* surface) +{ + for_each([&](std::shared_ptr const& observer) + { observer->surface_removed(surface); }); +} + +void ms::Observers::surfaces_reordered() +{ + for_each([&](std::shared_ptr const& observer) + { observer->surfaces_reordered(); }); +} + +void ms::Observers::scene_changed() +{ + for_each([&](std::shared_ptr const& observer) + { observer->scene_changed(); }); +} + +void ms::Observers::surface_exists(ms::Surface* surface) +{ + for_each([&](std::shared_ptr const& observer) + { observer->surface_exists(surface); }); +} + +void ms::Observers::end_observation() +{ + for_each([&](std::shared_ptr const& observer) + { observer->end_observation(); }); +} diff --git a/Source/Utilities/surface_stack.h b/Source/Utilities/surface_stack.h new file mode 100644 index 000000000..51d227b01 --- /dev/null +++ b/Source/Utilities/surface_stack.h @@ -0,0 +1,129 @@ +/* + * Copyright © 2012-2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Alan Griffiths + */ + +#ifndef MIR_SCENE_SURFACE_STACK_H_ +#define MIR_SCENE_SURFACE_STACK_H_ + +#include "Q:\Documents\GitHub\mir\include\server\mir\shell\surface_stack.h" + +#include "Q:\Documents\GitHub\mir\include\server\mir/compositor/scene.h" +#include "q:\Documents\GitHub\mir\include\server\mir\scene\observer.h" +#include "mir/input/scene.h" +#include "Q:\Documents\GitHub\mir\src\include\common\mir\recursive_read_write_mutex.h" + +#include "Q:\Documents\GitHub\mir\src\include\common\mir\basic_observers.h" + +#include +#include +#include +#include +#include +#include + +namespace mir +{ +namespace graphics +{ +class Renderable; +} +/// Management of Surface objects. Includes the model (SurfaceStack and Surface +/// classes) and controller (SurfaceController) elements of an MVC design. +namespace scene +{ +class InputRegistrar; +class BasicSurface; +class SceneReport; +class RenderingTracker; + +class Observers : public Observer, BasicObservers +{ +public: + // ms::Observer + void surface_added(Surface* surface) override; + void surface_removed(Surface* surface) override; + void surfaces_reordered() override; + void scene_changed() override; + void surface_exists(Surface* surface) override; + void end_observation() override; + + using BasicObservers::add; + using BasicObservers::remove; +}; + +class SurfaceStack : public compositor::Scene, public input::Scene, public shell::SurfaceStack +{ +public: + explicit SurfaceStack( + std::shared_ptr const& report); + virtual ~SurfaceStack() noexcept(true) {} + + // From Scene + compositor::SceneElementSequence scene_elements_for(compositor::CompositorID id) override; + int frames_pending(compositor::CompositorID) const override; + void register_compositor(compositor::CompositorID id) override; + void unregister_compositor(compositor::CompositorID id) override; + + // From Scene + void for_each(std::function const&)> const& callback) override; + + virtual void remove_surface(std::weak_ptr const& surface) override; + + virtual void raise(std::weak_ptr const& surface) override; + + void raise(SurfaceSet const& surfaces) override; + + void add_surface( + std::shared_ptr const& surface, + input::InputReceptionMode input_mode) override; + + auto surface_at(geometry::Point) const -> std::shared_ptr override; + + void add_observer(std::shared_ptr const& observer) override; + void remove_observer(std::weak_ptr const& observer) override; + + // Intended for input overlays, as described in mir::input::Scene documentation. + void add_input_visualization(std::shared_ptr const& overlay) override; + void remove_input_visualization(std::weak_ptr const& overlay) override; + + void emit_scene_changed() override; + +private: + SurfaceStack(const SurfaceStack&) = delete; + SurfaceStack& operator=(const SurfaceStack&) = delete; + void create_rendering_tracker_for(std::shared_ptr const&); + void update_rendering_tracker_compositors(); + + RecursiveReadWriteMutex mutable guard; + + std::shared_ptr const input_registrar; + std::shared_ptr const report; + + std::vector> surfaces; + std::map> rendering_trackers; + std::set registered_compositors; + + std::vector> overlays; + + Observers observers; + std::atomic scene_changed; +}; + +} +} + +#endif /* MIR_SCENE_SURFACE_STACK_H_ */ diff --git a/Source/Utilities/thread_safe_list.h b/Source/Utilities/thread_safe_list.h new file mode 100644 index 000000000..0522ae33a --- /dev/null +++ b/Source/Utilities/thread_safe_list.h @@ -0,0 +1,186 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authored By: Alan Griffiths + Alexandros Frantzis + */ + +#ifndef MIR_THREAD_SAFE_LIST_H_ +#define MIR_THREAD_SAFE_LIST_H_ + +#include "../recursive_read_write_mutex.h" + +#include + +namespace mir +{ + +/* + * Requirements for type 'Element' + * - for_each(): + * - copy-assignable + * - add(): + * - copy-assignable + * - operator bool: returns whether this is a valid element + * - remove(), remove_all(): + * - copy-assignable + * - Element{}: default construction should create an invalid element + * - bool operator==: equality of elements + * - bool operator!=: inequality of elements + * - clear(): + * - copy-assignable + * - Element{}: default construction should create an invalid element + */ + +template +class ThreadSafeList +{ +public: + void add(Element const& element); + void remove(Element const& element); + unsigned int remove_all(Element const& element); + void clear(); + void for_each(std::function const& f); + +private: + struct ListItem + { + ListItem() {} + RecursiveReadWriteMutex mutex; + Element element; + std::atomic next{nullptr}; + + ~ListItem() { delete next.load(); } + } head; +}; + +template +void ThreadSafeList::for_each( + std::function const& f) +{ + ListItem* current_item = &head; + + while (current_item) + { + RecursiveReadLock lock{current_item->mutex}; + + // We need to take a copy in case we recursively remove during call + if (auto const copy_of_element = current_item->element) f(copy_of_element); + + current_item = current_item->next; + } +} + +template +void ThreadSafeList::add(Element const& element) +{ + ListItem* current_item = &head; + + do + { + // Note: we release the read lock to avoid two threads calling add at + // the same time mutually blocking the other's upgrade to write lock. + { + RecursiveReadLock lock{current_item->mutex}; + if (current_item->element) continue; + } + + RecursiveWriteLock lock{current_item->mutex}; + + if (!current_item->element) + { + current_item->element = element; + return; + } + } + while (current_item->next && (current_item = current_item->next)); + + // No empty Items so append a new one + auto new_item = new ListItem; + new_item->element = element; + + for (ListItem* expected{nullptr}; + !current_item->next.compare_exchange_weak(expected, new_item); + expected = nullptr) + { + if (expected) current_item = expected; + } +} + +template +void ThreadSafeList::remove(Element const& element) +{ + ListItem* current_item = &head; + + do + { + { + RecursiveReadLock lock{current_item->mutex}; + if (current_item->element != element) continue; + } + + RecursiveWriteLock lock{current_item->mutex}; + + if (current_item->element == element) + { + current_item->element = Element{}; + return; + } + } + while ((current_item = current_item->next)); +} + +template +unsigned int ThreadSafeList::remove_all(Element const& element) +{ + ListItem* current_item = &head; + auto removed = 0u; + + do + { + { + RecursiveReadLock lock{current_item->mutex}; + if (current_item->element != element) continue; + } + + RecursiveWriteLock lock{current_item->mutex}; + + if (current_item->element == element) + { + current_item->element = Element{}; + ++removed; + } + } + while ((current_item = current_item->next)); + + return removed; +} + +template +void ThreadSafeList::clear() +{ + ListItem* current_item = &head; + + do + { + RecursiveWriteLock lock{current_item->mutex}; + current_item->element = Element{}; + } + while ((current_item = current_item->next)); +} + +} + +#endif /* MIR_THREAD_SAFE_LIST_H_ */ From 0316548d891389e6487fd97cc071351515b9570a Mon Sep 17 00:00:00 2001 From: rsjaffe Date: Thu, 14 Jul 2016 17:20:38 -0700 Subject: [PATCH 8/8] update version to 1.3.0.1 --- Builds/MacOSX/Info-App.plist | 4 ++-- Builds/MacOSX/MIDI2LR.xcodeproj/project.pbxproj | 8 ++++---- Builds/VisualStudio2015/MIDI2LR.vcxproj | 4 ++-- Builds/VisualStudio2015/resources.rc | 6 +++--- JuceLibraryCode/JuceHeader.h | 4 ++-- MIDI2LR.jucer | 2 +- Source/LRPlugin/MIDI2LR.lrplugin/Info.lua | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Builds/MacOSX/Info-App.plist b/Builds/MacOSX/Info-App.plist index bc61cc711..94a2d281f 100644 --- a/Builds/MacOSX/Info-App.plist +++ b/Builds/MacOSX/Info-App.plist @@ -18,9 +18,9 @@ CFBundleSignature ???? CFBundleShortVersionString - 1.3.0.0 + 1.3.0.1 CFBundleVersion - 1.3.0.0 + 1.3.0.1 NSHumanReadableCopyright NSHighResolutionCapable diff --git a/Builds/MacOSX/MIDI2LR.xcodeproj/project.pbxproj b/Builds/MacOSX/MIDI2LR.xcodeproj/project.pbxproj index 2573c32a2..60916f9b2 100644 --- a/Builds/MacOSX/MIDI2LR.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/MIDI2LR.xcodeproj/project.pbxproj @@ -2450,8 +2450,8 @@ "_NDEBUG=1", "NDEBUG=1", "JUCER_XCODE_MAC_F6D2F4CF=1", - "JUCE_APP_VERSION=1.3.0.0", - "JUCE_APP_VERSION_HEX=0x1030000", ); + "JUCE_APP_VERSION=1.3.0.1", + "JUCE_APP_VERSION_HEX=0x1030001", ); GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; HEADER_SEARCH_PATHS = ("../../JuceLibraryCode", "../../JuceLibraryCode/modules", "$(inherited)"); @@ -2476,8 +2476,8 @@ "_NDEBUG=1", "NDEBUG=1", "JUCER_XCODE_MAC_F6D2F4CF=1", - "JUCE_APP_VERSION=1.3.0.0", - "JUCE_APP_VERSION_HEX=0x1030000", ); + "JUCE_APP_VERSION=1.3.0.1", + "JUCE_APP_VERSION_HEX=0x1030001", ); GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; HEADER_SEARCH_PATHS = ("../../JuceLibraryCode", "../../JuceLibraryCode/modules", "$(inherited)"); diff --git a/Builds/VisualStudio2015/MIDI2LR.vcxproj b/Builds/VisualStudio2015/MIDI2LR.vcxproj index 148bfe010..92ab1f9bd 100644 --- a/Builds/VisualStudio2015/MIDI2LR.vcxproj +++ b/Builds/VisualStudio2015/MIDI2LR.vcxproj @@ -56,7 +56,7 @@ Disabled EditAndContinue ..\..\JuceLibraryCode;..\..\JuceLibraryCode\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.3.0.0;JUCE_APP_VERSION_HEX=0x1030000;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.3.0.1;JUCE_APP_VERSION_HEX=0x1030001;%(PreprocessorDefinitions) MultiThreadedDebug true @@ -97,7 +97,7 @@ Full ..\..\JuceLibraryCode;..\..\JuceLibraryCode\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.3.0.0;JUCE_APP_VERSION_HEX=0x1030000;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=1.3.0.1;JUCE_APP_VERSION_HEX=0x1030001;%(PreprocessorDefinitions) MultiThreaded true diff --git a/Builds/VisualStudio2015/resources.rc b/Builds/VisualStudio2015/resources.rc index 2621590fc..7430ca568 100644 --- a/Builds/VisualStudio2015/resources.rc +++ b/Builds/VisualStudio2015/resources.rc @@ -7,16 +7,16 @@ #include VS_VERSION_INFO VERSIONINFO -FILEVERSION 1,3,0,0 +FILEVERSION 1,3,0,1 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "FileDescription", "MIDI2LR\0" - VALUE "FileVersion", "1.3.0.0\0" + VALUE "FileVersion", "1.3.0.1\0" VALUE "ProductName", "MIDI2LR\0" - VALUE "ProductVersion", "1.3.0.0\0" + VALUE "ProductVersion", "1.3.0.1\0" END END diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index e83921698..013c1bdf0 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -37,8 +37,8 @@ namespace ProjectInfo { const char* const projectName = "MIDI2LR"; - const char* const versionString = "1.3.0.0"; - const int versionNumber = 0x1030000; + const char* const versionString = "1.3.0.1"; + const int versionNumber = 0x1030001; } #endif diff --git a/MIDI2LR.jucer b/MIDI2LR.jucer index e21a1abc2..5ca0b514a 100644 --- a/MIDI2LR.jucer +++ b/MIDI2LR.jucer @@ -1,6 +1,6 @@ - diff --git a/Source/LRPlugin/MIDI2LR.lrplugin/Info.lua b/Source/LRPlugin/MIDI2LR.lrplugin/Info.lua index fc22a77f9..d22becaab 100644 --- a/Source/LRPlugin/MIDI2LR.lrplugin/Info.lua +++ b/Source/LRPlugin/MIDI2LR.lrplugin/Info.lua @@ -46,5 +46,5 @@ return { }, --]] }, - VERSION = { major=1, minor=3, revision=0, build=0} + VERSION = { major=1, minor=3, revision=0, build=1} }