Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion include/tev/HelpWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@

namespace tev {

struct Ipc;

class HelpWindow : public nanogui::Window {
public:
HelpWindow(nanogui::Widget* parent, bool supportsHdr, std::function<void()> closeCallback);
HelpWindow(nanogui::Widget* parent, bool supportsHdr, const Ipc& ipc, std::function<void()> closeCallback);

bool keyboard_event(int key, int scancode, int action, int modifiers) override;

Expand Down
12 changes: 11 additions & 1 deletion include/tev/ImageViewer.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <tev/Image.h>
#include <tev/ImageButton.h>
#include <tev/ImageCanvas.h>
#include <tev/Ipc.h>
#include <tev/Lazy.h>
#include <tev/MultiGraph.h>
#include <tev/SharedQueue.h>
Expand All @@ -41,7 +42,14 @@ namespace tev {

class ImageViewer : public nanogui::Screen {
public:
ImageViewer(const std::shared_ptr<BackgroundImagesLoader>& imagesLoader, bool maximize, bool showUi, bool floatBuffer, bool supportsHdr);
ImageViewer(
const std::shared_ptr<BackgroundImagesLoader>& imagesLoader,
const std::shared_ptr<Ipc>& ipc,
bool maximize,
bool showUi,
bool floatBuffer,
bool supportsHdr
);

bool mouse_button_event(const nanogui::Vector2i& p, int button, bool down, int modifiers) override;
bool mouse_motion_event(const nanogui::Vector2i& p, const nanogui::Vector2i& rel, int button, int modifiers) override;
Expand Down Expand Up @@ -158,6 +166,7 @@ class ImageViewer : public nanogui::Screen {
template <typename T> void scheduleToUiThread(const T& fun) { mTaskQueue.push(fun); }

BackgroundImagesLoader& imagesLoader() const { return *mImagesLoader; }
Ipc& ipc() const { return *mIpc; }

private:
void updateFilter();
Expand Down Expand Up @@ -211,6 +220,7 @@ class ImageViewer : public nanogui::Screen {
nanogui::Widget* mMetricButtonContainer;

std::shared_ptr<BackgroundImagesLoader> mImagesLoader;
std::shared_ptr<Ipc> mIpc;

std::shared_ptr<Image> mCurrentImage;
std::shared_ptr<Image> mCurrentReference;
Expand Down
17 changes: 15 additions & 2 deletions include/tev/Ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,13 +253,22 @@ class Ipc {
Ipc(const std::string& hostname = "127.0.0.1:14158");
virtual ~Ipc();

bool isPrimaryInstance() { return mIsPrimaryInstance; }
bool isPrimaryInstance() const { return mIsPrimaryInstance; }
bool isConnectedToPrimaryInstance() const;

bool attemptToBecomePrimaryInstance();

void sendToPrimaryInstance(const IpcPacket& message);
void receiveFromSecondaryInstance(std::function<void(const IpcPacket&)> callback);

std::string ip() const { return mIp; }
std::string port() const { return mPort; }
std::string hostname() const;

size_t nActiveConnections() const { return mSocketConnections.size(); }
size_t nTotalBytesSent() const { return mNTotalBytesSent; }
size_t nTotalBytesReceived() const { return mNTotalBytesReceived; }

private:
bool mIsPrimaryInstance;
socket_t mSocketFd;
Expand All @@ -275,7 +284,8 @@ class Ipc {
public:
SocketConnection(Ipc::socket_t fd, const std::string& name);

void service(std::function<void(const IpcPacket&)> callback);
// Servicing a connection also returns the total number of bytes received
size_t service(std::function<void(const IpcPacket&)> callback);

void close();

Expand All @@ -297,6 +307,9 @@ class Ipc {
std::string mIp;
std::string mPort;
std::string mLockName;

size_t mNTotalBytesSent = 0;
size_t mNTotalBytesReceived = 0;
};

} // namespace tev
44 changes: 33 additions & 11 deletions src/HelpWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

#include <tev/HelpWindow.h>
#include <tev/Ipc.h>

#include <nanogui/button.h>
#include <nanogui/icons.h>
Expand Down Expand Up @@ -45,7 +46,7 @@ string HelpWindow::ALT = "Opt";
string HelpWindow::ALT = "Alt";
#endif

HelpWindow::HelpWindow(Widget* parent, bool supportsHdr, function<void()> closeCallback) :
HelpWindow::HelpWindow(Widget* parent, bool supportsHdr, const Ipc& ipc, function<void()> closeCallback) :
Window{parent, "Help"}, mCloseCallback{closeCallback} {

auto closeButton = new Button{button_panel(), "", FA_TIMES};
Expand Down Expand Up @@ -154,17 +155,43 @@ HelpWindow::HelpWindow(Widget* parent, bool supportsHdr, function<void()> closeC
addRow(ui, "Escape", "Reset find string");
addRow(ui, COMMAND + "+Q", "Quit");

// About tab
Widget* about = new Widget(tabWidget);
about->set_layout(new GroupLayout{});
tabWidget->append_tab("About", about);

auto addText = [](Widget* current, string text, string font = "sans", int fontSize = 18) {
auto row = new Widget{current};
row->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Middle, 0, 10});
new Label{row, text, font, fontSize};
};

auto addSpacer = [](Widget* current, int space) {
auto row = new Widget{current};
row->set_height(space);
};

// Network tab
Widget* network = new Widget(tabWidget);
network->set_layout(new GroupLayout{});
tabWidget->append_tab("Network", network);

new Label{network, "Inter process communication (IPC)", "sans-bold", 18};
auto ipcSection = new Widget{network};
ipcSection->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

if (ipc.isPrimaryInstance()) {
addRow(network, fmt::format("Listening to {}", ipc.hostname()), "Status");
addRow(network, to_string(ipc.nActiveConnections()), "Active connections");
} else if (ipc.isConnectedToPrimaryInstance()) {
addRow(network, fmt::format("Connected to {}", ipc.hostname()), "Status");
} else {
addRow(network, fmt::format("Failed to connect to {}", ipc.hostname()), "Status");
}

addRow(network, to_string(ipc.nTotalBytesSent()), "Bytes sent");
addRow(network, to_string(ipc.nTotalBytesReceived()), "Bytes received");

// About tab
Widget* about = new Widget(tabWidget);
about->set_layout(new GroupLayout{});
tabWidget->append_tab("About", about);

auto addLibrary = [](Widget* current, string name, string desc) {
auto row = new Widget{current};
row->set_layout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 3, 30});
Expand All @@ -176,11 +203,6 @@ HelpWindow::HelpWindow(Widget* parent, bool supportsHdr, function<void()> closeC
new Label{row, desc, "sans", 18};
};

auto addSpacer = [](Widget* current, int space) {
auto row = new Widget{current};
row->set_height(space);
};

addSpacer(about, 5);

addText(about, "tev — The EXR Viewer", "sans-bold", 46);
Expand Down
21 changes: 14 additions & 7 deletions src/ImageViewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ static const int SIDEBAR_MIN_WIDTH = 230;
static const float CROP_MIN_SIZE = 3;

ImageViewer::ImageViewer(
const shared_ptr<BackgroundImagesLoader>& imagesLoader, bool maximize, bool showUi, bool floatBuffer, bool /*supportsHdr*/
const shared_ptr<BackgroundImagesLoader>& imagesLoader, const shared_ptr<Ipc>& ipc, bool maximize, bool showUi, bool floatBuffer, bool /*supportsHdr*/
) :
nanogui::Screen{
nanogui::Vector2i{1024, 799},
"tev", true, maximize, false, true, true, floatBuffer
},
mImagesLoader{imagesLoader} {
mImagesLoader{imagesLoader},
mIpc{ipc} {

if (floatBuffer && !m_float_buffer) {
tlog::warning() << "Failed to create floating point frame buffer.";
}
Expand Down Expand Up @@ -1699,9 +1701,7 @@ bool ImageViewer::setFilter(const string& filter) {
return true;
}

void ImageViewer::setFps(int value) {
mFpsTextBox->set_value(value);
}
void ImageViewer::setFps(int value) { mFpsTextBox->set_value(value); }

bool ImageViewer::useRegex() const { return mRegexButton->pushed(); }

Expand Down Expand Up @@ -1743,7 +1743,7 @@ void ImageViewer::toggleHelpWindow() {
mHelpWindow = nullptr;
mHelpButton->set_pushed(false);
} else {
mHelpWindow = new HelpWindow{this, mSupportsHdr, [this] { toggleHelpWindow(); }};
mHelpWindow = new HelpWindow{this, mSupportsHdr, ipc(), [this] { toggleHelpWindow(); }};
mHelpWindow->center();
mHelpWindow->request_focus();
mHelpButton->set_pushed(true);
Expand Down Expand Up @@ -2010,7 +2010,14 @@ void ImageViewer::updateTitle() {
}

caption += fmt::format(
" – @{},{} ({:.3f},{:.3f}) / {}x{}: {}", imageCoords.x(), imageCoords.y(), imageCoords.x() / (double)mCurrentImage->size().x(), imageCoords.y() / (double)mCurrentImage->size().y(), mCurrentImage->size().x(), mCurrentImage->size().y(), valuesString
" – @{},{} ({:.3f},{:.3f}) / {}x{}: {}",
imageCoords.x(),
imageCoords.y(),
imageCoords.x() / (double)mCurrentImage->size().x(),
imageCoords.y() / (double)mCurrentImage->size().y(),
mCurrentImage->size().x(),
mCurrentImage->size().y(),
valuesString
);
}

Expand Down
24 changes: 19 additions & 5 deletions src/Ipc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,10 @@ Ipc::~Ipc() {
#endif
}

bool Ipc::isConnectedToPrimaryInstance() const {
return !mIsPrimaryInstance && mSocketFd != INVALID_SOCKET;
}

bool Ipc::attemptToBecomePrimaryInstance() {
#ifdef _WIN32
// Make sure at most one instance of tev is running
Expand Down Expand Up @@ -558,6 +562,8 @@ void Ipc::sendToPrimaryInstance(const IpcPacket& message) {
if (bytesSent != int(message.size())) {
throw runtime_error{fmt::format("send() failed: {}", errorString(lastSocketError()))};
}

mNTotalBytesSent += message.size();
}

void Ipc::receiveFromSecondaryInstance(function<void(const IpcPacket&)> callback) {
Expand Down Expand Up @@ -587,7 +593,7 @@ void Ipc::receiveFromSecondaryInstance(function<void(const IpcPacket&)> callback
// Service existing connections.
for (auto iter = mSocketConnections.begin(); iter != mSocketConnections.end();) {
auto cur = iter++;
cur->service(callback);
mNTotalBytesReceived += cur->service(callback);

// If the connection became closed, stop keeping track of it.
if (cur->isClosed()) {
Expand All @@ -596,6 +602,10 @@ void Ipc::receiveFromSecondaryInstance(function<void(const IpcPacket&)> callback
}
}

string Ipc::hostname() const {
return fmt::format("{}:{}", ip(), port());
}

Ipc::SocketConnection::SocketConnection(Ipc::socket_t fd, const string& name) : mSocketFd{fd}, mName{name} {
TEV_ASSERT(mSocketFd != INVALID_SOCKET, "SocketConnection must receive a valid socket.");

Expand All @@ -605,12 +615,13 @@ Ipc::SocketConnection::SocketConnection(Ipc::socket_t fd, const string& name) :
mBuffer.resize(1024 * 1024);
}

void Ipc::SocketConnection::service(function<void(const IpcPacket&)> callback) {
size_t Ipc::SocketConnection::service(function<void(const IpcPacket&)> callback) {
if (isClosed()) {
// Client disconnected, so don't bother.
return;
return 0;
}

size_t nTotalBytesReceived = 0;
while (true) {
// Receive as much data as we can, up to the capacity of 'mBuffer'.
size_t maxBytes = mBuffer.size() - mRecvOffset;
Expand All @@ -623,19 +634,20 @@ void Ipc::SocketConnection::service(function<void(const IpcPacket&)> callback) {
} else {
tlog::warning() << "Error while reading from socket. " << errorString(errorId) << " Connection terminated.";
close();
return;
break;
}
}

TEV_ASSERT(bytesReceived >= 0, "With no error, the number of bytes received should be positive.");
mRecvOffset += (size_t)bytesReceived;
nTotalBytesReceived += (size_t)bytesReceived;

// Since we aren't getting annoying SIGPIPE signals when a client disconnects, a zero-byte read here is how we know when that
// happens.
if (bytesReceived == 0) {
tlog::info() << "Client " << mName << " (#" << mSocketFd << ") disconnected";
close();
return;
break;
}

// Go through the buffer and service as many complete messages as we can find.
Expand Down Expand Up @@ -668,6 +680,8 @@ void Ipc::SocketConnection::service(function<void(const IpcPacket&)> callback) {
mRecvOffset -= processedOffset;
}
}

return nTotalBytesReceived;
}

void Ipc::SocketConnection::close() {
Expand Down
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ static int mainFunc(const vector<string>& arguments) {
}

// sImageViewer is a raw pointer to make sure it will never get deleted. nanogui crashes upon cleanup, so we better not try.
sImageViewer = new ImageViewer{imagesLoader, maximize, !hideUiFlag, capability10bit || capabilityEdr, capabilityEdr};
sImageViewer = new ImageViewer{imagesLoader, ipc, maximize, !hideUiFlag, capability10bit || capabilityEdr, capabilityEdr};
imageViewerIsReady = true;

sImageViewer->draw_all();
Expand Down