Skip to content

Commit

Permalink
Refactor grid state management and add grid mirror mode (#176)
Browse files Browse the repository at this point in the history
* Remove firmware products in res/ on make clean

* Remove serialosc pre-init on module widget load

* Prototype of virtual grid mirror mode

* null-init mirror consumer gridConnection properly

* Move mirror options to a submenu & only list hardware

* Create GridConsumerBase for shared implementation of IGridConsumer; persist virtual grid mirror settings in patch
  • Loading branch information
Dewb authored Sep 17, 2023
1 parent a745e86 commit 7acc876
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 119 deletions.
7 changes: 7 additions & 0 deletions src/common/core/ActionQueue.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include "rack.hpp"
#include <functional>

typedef std::function<void(void)> Action;
typedef rack::dsp::RingBuffer<Action, 4> ActionQueue;
18 changes: 7 additions & 11 deletions src/common/core/GridConnection/GridConnection.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
#include "GridConnection.hpp"

Grid::~Grid()
{
}

void GridConnectionManager::registerGrid(Grid* grid)
{
grids.insert(grid);

// Check if there's a consumer with a saved connection looking for this grid
GridConsumer* consumerToConnect = nullptr;
IGridConsumer* consumerToConnect = nullptr;
for (auto consumer : consumers)
{
if (!isConnected(consumer) && grid->getDevice().id == consumer->gridGetLastDeviceId(true))
Expand All @@ -25,7 +21,7 @@ void GridConnectionManager::registerGrid(Grid* grid)
}
}

void GridConnectionManager::registerGridConsumer(GridConsumer* consumer)
void GridConnectionManager::registerGridConsumer(IGridConsumer* consumer)
{
consumers.insert(consumer);

Expand Down Expand Up @@ -66,13 +62,13 @@ void GridConnectionManager::deregisterGrid(std::string id, bool deleteGrid)
}
}

void GridConnectionManager::deregisterGridConsumer(GridConsumer* consumer)
void GridConnectionManager::deregisterGridConsumer(IGridConsumer* consumer)
{
disconnect(consumer);
consumers.erase(consumer);
}

void GridConnectionManager::connect(Grid* grid, GridConsumer* consumer)
void GridConnectionManager::connect(Grid* grid, IGridConsumer* consumer)
{
disconnect(consumer, true);
disconnect(grid, true);
Expand All @@ -82,7 +78,7 @@ void GridConnectionManager::connect(Grid* grid, GridConsumer* consumer)
consumer->gridConnected(grid);
}

bool GridConnectionManager::isConnected(GridConsumer* consumer)
bool GridConnectionManager::isConnected(IGridConsumer* consumer)
{
return consumerToGridMap.find(consumer) != consumerToGridMap.end();
}
Expand All @@ -100,15 +96,15 @@ void GridConnectionManager::disconnect(Grid* grid, bool ownerChanged)
auto iter = idToConsumerMap.find(grid->getDevice().id);
if (iter != idToConsumerMap.end())
{
GridConsumer* consumer = iter->second;
auto consumer = iter->second;
consumer->gridDisconnected(ownerChanged);
idToConsumerMap.erase(grid->getDevice().id);
consumerToGridMap.erase(consumer);
}
}
}

void GridConnectionManager::disconnect(GridConsumer* consumer, bool ownerChanged)
void GridConnectionManager::disconnect(IGridConsumer* consumer, bool ownerChanged)
{
if (consumer)
{
Expand Down
24 changes: 14 additions & 10 deletions src/common/core/GridConnection/GridConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,40 @@ struct SerialOscInterface;

struct Grid
{
virtual ~Grid();
virtual ~Grid() {}
virtual const MonomeDevice& getDevice() = 0;
virtual void updateRow(int x_offset, int y, uint8_t bitfield) = 0;
virtual void updateQuadrant(int x, int y, uint8_t* leds) = 0;
virtual void updateRing(int n, uint8_t leds[64]) = 0;
virtual void clearAll() = 0;
virtual bool isHardware() = 0;
};

struct GridConsumer
struct IGridConsumer
{
virtual ~IGridConsumer() {}
virtual void gridConnected(Grid* grid) = 0;
virtual void gridDisconnected(bool ownerChanged) = 0;
virtual std::string gridGetCurrentDeviceId() = 0;
virtual std::string gridGetLastDeviceId(bool owned) = 0;
virtual void gridButtonEvent(int x, int y, bool state) = 0;
virtual void encDeltaEvent(int n, int d) = 0;
virtual Grid* gridGetDevice() = 0;
};

struct GridConnectionManager final
{
// Only call these from the UI thread
void registerGrid(Grid* grid);
void registerGridConsumer(GridConsumer* consumer);
void registerGridConsumer(IGridConsumer* consumer);
void deregisterGrid(std::string id, bool deleteGrid = false);
void deregisterGridConsumer(GridConsumer* consumer);
void deregisterGridConsumer(IGridConsumer* consumer);

void connect(Grid* grid, GridConsumer* consumer);
bool isConnected(GridConsumer* consumer);
void connect(Grid* grid, IGridConsumer* consumer);
bool isConnected(IGridConsumer* consumer);
bool isConnected(std::string id);
void disconnect(Grid* grid, bool ownerChanged = false);
void disconnect(GridConsumer* consumer, bool ownerChanged = false);
void disconnect(IGridConsumer* consumer, bool ownerChanged = false);

void dispatchButtonMessage(MonomeDevice* device, int x, int y, bool state);
void dispatchEncDeltaMessage(MonomeDevice* device, int n, int d);
Expand All @@ -54,8 +58,8 @@ struct GridConnectionManager final
GridConnectionManager(GridConnectionManager&&) = delete;
GridConnectionManager&& operator=(const GridConnectionManager&&) = delete;

std::set<GridConsumer*> consumers;
std::set<IGridConsumer*> consumers;
std::set<Grid*> grids;
std::map<std::string, GridConsumer*> idToConsumerMap;
std::map<GridConsumer*, Grid*> consumerToGridMap;
std::map<std::string, IGridConsumer*> idToConsumerMap;
std::map<IGridConsumer*, Grid*> consumerToGridMap;
};
126 changes: 126 additions & 0 deletions src/common/core/GridConnection/GridConnectionMenu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include "GridConnectionMenu.hpp"
#include "SerialOscInterface.hpp"

using namespace rack;

struct NewConnectGridItem : rack::ui::MenuItem
{
Grid* grid;
IGridConsumer* consumer;
ActionQueue* actionQueue;

void onAction(const rack::event::Action& e) override
{
if (actionQueue)
{
auto thisGrid = grid;
auto thisConsumer = consumer;

actionQueue->push([thisGrid, thisConsumer]()
{ GridConnectionManager::get().connect(thisGrid, thisConsumer); });
}
}
};

void menuUserReacquireGrid(IGridConsumer* consumer, std::string lastDeviceId, ActionQueue* actionQueue)
{
for (Grid* grid : GridConnectionManager::get().getGrids())
{
if (lastDeviceId == grid->getDevice().id)
{
if (actionQueue)
{
actionQueue->push([grid, consumer]()
{
GridConnectionManager::get().connect(grid, consumer);
}
);
}
return;
}
}
}

void appendDeviceConnectionMenu(rack::Menu* menu, IGridConsumer* consumer, ActionQueue* actionQueue, bool hardwareOnly)
{
std::string currentConnectedDeviceId = consumer->gridGetCurrentDeviceId();
std::string lastConnectedDeviceId = consumer->gridGetLastDeviceId(false);

menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Device Connection"));

if (SerialOscInterface::get()->isServiceDetected())
{
menu->addChild(
construct<MenuLabel>(
&MenuLabel::text,
"serialosc version " + SerialOscInterface::get()->getServiceVersion()));
}
else
{
menu->addChild(
createMenuItem("└ serialosc service not detected, click here to install", "",
[=]()
{
system::openBrowser("https://monome.org/docs/serialosc/setup/");
}));
}

// enumerate registered grid devices
int deviceCount = 0;
bool preferredDeviceFound = false;
for (Grid* grid : GridConnectionManager::get().getGrids())
{
if (hardwareOnly && !grid->isHardware())
{
continue;
}

auto connectItem = new NewConnectGridItem();
connectItem->text = "" + grid->getDevice().type + " (" + grid->getDevice().id + ") ";

auto rightText = "";
if (currentConnectedDeviceId == grid->getDevice().id)
{
rightText = "";
preferredDeviceFound = true;
}
else if (currentConnectedDeviceId == "" && lastConnectedDeviceId == grid->getDevice().id)
{
rightText = "";
preferredDeviceFound = true;
}

connectItem->rightText = rightText;
connectItem->grid = grid;
connectItem->actionQueue = actionQueue;
connectItem->consumer = consumer;

menu->addChild(connectItem);
deviceCount++;
}

if (deviceCount == 0)
{
std::string message = hardwareOnly ? "no hardware devices found" : "no hardware or virtual devices found";
menu->addChild(construct<MenuLabel>(&MenuLabel::text, " (" + message + ")"));
}

if (currentConnectedDeviceId == "" && lastConnectedDeviceId != "")
{
if (preferredDeviceFound)
{
menu->addChild(createMenuItem("Reacquire grid", "", [=]()
{
if (lastConnectedDeviceId != "")
{
menuUserReacquireGrid(consumer, lastConnectedDeviceId, actionQueue);
}
}
));
}
else
{
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Can't reacquire grid (" + lastConnectedDeviceId + " not found)"));
}
}
}
7 changes: 7 additions & 0 deletions src/common/core/GridConnection/GridConnectionMenu.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include "rack.hpp"
#include "GridConnection.hpp"
#include "ActionQueue.hpp"

void appendDeviceConnectionMenu(rack::Menu* menu, IGridConsumer* consumer, ActionQueue* queue, bool hardwareOnly = false);
106 changes: 106 additions & 0 deletions src/common/core/GridConnection/GridConsumerBase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include "GridConsumerBase.hpp"

GridConsumerBase::GridConsumerBase()
: lastConnectedDeviceId("")
, currentConnectedDeviceId("")
, gridConnection(nullptr)
{

}

void GridConsumerBase::gridConnected(Grid* newConnection)
{
gridConnection = newConnection;
if (gridConnection)
{
std::string id = gridConnection->getDevice().id;
lastConnectedDeviceId = id;
currentConnectedDeviceId = id;
connectionOwned = true;
}
}

void GridConsumerBase::gridDisconnected(bool ownerChanged)
{
gridConnection = nullptr;
currentConnectedDeviceId = "";

if (ownerChanged)
{
connectionOwned = false;
}
}

std::string GridConsumerBase::gridGetCurrentDeviceId()
{
return currentConnectedDeviceId;
}

std::string GridConsumerBase::gridGetLastDeviceId(bool owned)
{
if (owned && !connectionOwned)
{
return "";
}

return lastConnectedDeviceId;
}

Grid* GridConsumerBase::gridGetDevice()
{
return gridConnection;
}

void GridConsumerBase::userReacquireGrid()
{
if (lastConnectedDeviceId != "" && gridConnection == nullptr)
{
for (Grid* grid : GridConnectionManager::get().getGrids())
{
if (gridGetLastDeviceId(false) == grid->getDevice().id)
{
GridConnectionManager::get().connect(grid, this);
return;
}
}
}
}

void GridConsumerBase::toggleGridConnection(Grid* grid)
{
if (gridConnection == grid)
{
GridConnectionManager::get().disconnect(this, true);
lastConnectedDeviceId = "";
}
else
{
GridConnectionManager::get().connect(grid, this);
}
}

void GridConsumerBase::saveGridConnectionToJson(json_t* rootJ)
{
std::string deviceId = lastConnectedDeviceId;
if (gridConnection)
{
deviceId = gridConnection->getDevice().id;
}
json_object_set_new(rootJ, "connectedDeviceId", json_string(deviceId.c_str()));
json_object_set_new(rootJ, "connectionOwned", json_boolean(connectionOwned));
}

void GridConsumerBase::loadGridConnectionFromJson(json_t* rootJ)
{
json_t* id = json_object_get(rootJ, "connectedDeviceId");
if (id)
{
lastConnectedDeviceId = json_string_value(id);
}

json_t* owned = json_object_get(rootJ, "connectionOwned");
if (owned)
{
connectionOwned = json_boolean_value(owned);
}
}
Loading

0 comments on commit 7acc876

Please sign in to comment.