ofAddListener and multiple instances

Hi,

I’ve got an application requiring two or more windows. There’s the main application, which has a window created the standard way with an ofApp running in it, and a number of other windows, which have a simple wrapper class around them. The wrapper only really has a window and an id, which is a number.

Similarly to the examples, these listen for their window’s ‘draw’ event, and then call a function. That function then calls the main application’s “drawToSecondaryWindow” function, with a pointer to the wrapper as an argument.

Here’s the code for the wrapper class:

void SecondaryAppWindow::setup (
  shared_ptr<Screensaver> _mainApp,
  shared_ptr<ofAppBaseWindow> _mainWindow,
  MonitorDefinition _m,
  int _id
) {
  mainApp = _mainApp;
  mainWindow = _mainWindow;
  m = _m;
  id = _id;

  ofGLFWWindowSettings settings;
  settings.setGLVersion(3, 2);
  settings.decorated = false;
  settings.shareContextWith = mainWindow;

  settings.setSize(m.resolution.x, m.resolution.y);
  settings.setPosition(m.offset);
  
  window = ofCreateWindow(settings);
  window->setVerticalSync(false);

  this->addListeners();
}

void SecondaryAppWindow::addListeners() {
  ofAddListener(window->events().draw, this, &SecondaryAppWindow::onFrameEvent);
  ofAddListener(window->events().keyPressed, this, &SecondaryAppWindow::onKeyboardInteraction);
  ofAddListener(window->events().keyReleased, this, &SecondaryAppWindow::onKeyboardInteraction); 
  //...etc
}

void SecondaryAppWindow::onFrameEvent(ofEventArgs &args) {
  this->mainApp->drawToSecondaryAppWindow(this); // Here's where the problem seems to be
}

and then in the ofApp

void ofApp::drawToSecondaryAppWindow(SecondaryAppWindow * s) {
 
  cout << s->id << endl; // this always seems to get the same value, regardless of how many there are

//etc
}

The problem is, that in the main application’s drawToSecondaryAppWindow function, the passed in instance always seems to be the last one created. If there’s three windows, the id is always of the third; four, and it’s the id of the fourth.

Have I just set this up wrong, or do event listeners like this only support one instance at a time? If the latter, is there any way that I can rework this? I’d like to be able to draw different things in different windows based on a single value that identifies the window, because the number of windows is going to vary wildly.

Thanks in advance!

Hi @Godwhacker,

I had similar issues before, and I seem to remember that i would have recreated the important events manually, just so i could add the index of the window in. Therefore, all instances would listen to the same events, but during parsing they would only react to their index one.
something like that:

ofEvent < std::vector < float > > 

Also, I think fact that you can only share Context with the main window in the context of creating the ofApp, so you have to instanciate it in the main.cpp - will try to find an example - .
Do you have the code of the ofApp.cpp?

Hope this helps,

Best,

P

Thanks for that-

I’m creating each of these secondary windows (at the minute) in main.cpp, like this:

for (int i = 1; i < appSettings.monitors.size(); i++) {
    SecondaryAppWindow w;
    w.setup(mainApp, mainWindow, appSettings.monitors[i], i);
  }

These objects are being created, but not stored anywhere, though I’m assuming that doesn’t matter? C++ is still a bit new to me, it’s always been the big scary language I’ve avoided using.

The context sharing stuff seems to be fine- I’ve a separate issue with drawing multiple FBOs at once, but one thing at a time.

I’m not sure it doesn’t matter, because in this case, they would only exist in your for loop…

Try putting them in a list which belongs to the ofApp instance :
in ofApp.h

vector <  SecondaryAppWindow > sWindows;

Then in main.cpp :

// [...]
w.setup(mainApp, mainWindow, appSettings.monitors[i], i);
sWindows.push_back(w);

Let me know if this helps,

What is your poroblem with the FBOs?

Best,
P

1 Like

I’ll give it a go in a bit, thanks! Unfortunately I have some (paid, yet boring) work to finish first

Regarding the FBOs, I’m allocating one per window. Right now they cover the whole of each monitor I have attached. The problem is that if I try drawing any of the FBOs in the drawToSecondaryAppWindow function that are not also drawn to screen in draw, they come out blank. The one that I draw to screen however can be drawn into the other windows quite happily

Here’s the main draw code:

void ofApp::draw() {

    for (int i = 0; i < windowFBOs.size(); i++) {

    glm::mat4 * projection = &projections[i];
    ofFbo* fbo = &windowFBOs[i];
    fbo->begin();
    shader.begin();
    ofBackground(0);
    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    shader.setUniform1f("wMin", -1.0f);
    shader.setUniform1f("wMax", 1.0f);
    //etc etc...

    mainVbo.draw(GL_LINES, numFaceVertices, numEdgeVertices);
    shader.setUniform1i("useMatrix", 0);
    feedbackRenderVbo.drawElements(GL_LINE_STRIP, numTrails * pointsPerTrail + numTrails);
    shader.end();
    fbo->end();

  }
  
  // Note: drawing the first here, but I can change this to the second or third and those will get drawn
  windowFBOs[0].draw(glm::vec2(0, 0));
}

and then in the drawToSecondaryAppWindow function:

void ofApp::drawToSecondaryAppWindow(SecondaryAppWindow * s) {
  windowFBOs[0].draw(glm::vec2(0, 0)); // This draws an image into the window
//  windowFBOs[1].draw(glm::vec2(0, 0)); // This does not
}

Does that make sense? I’m not quite sure of the order for the begin / end sections for drawing to multiple FBOs.

Updated to add:

If I draw all the FBOs at the end of the draw call, i.e.

  windowFBOs[2].draw(glm::vec2(0, 0));
  windowFBOs[1].draw(glm::vec2(0, 0));
  windowFBOs[0].draw(glm::vec2(0, 0));

Then they render to the other screens fine. But I don’t see why I have to draw them in the main draw function at all.

Just tried this-

I’m not sure it doesn’t matter, because in this case, they would only exist in your for loop…
Try putting them in a list which belongs to the ofApp instance :

Same result, unfortunately, even when storing the references.

Hi,

you definitely need to store those SecondaryAppWindow instances.
When you use ofAddListener(...) you then need to call ofRemoveListener(...) otherwise you end with some listeners pointing where these should not. This makes sense with the behavior you describe, as in each iteration of the for loop the newly created object will get destroyed but will not get removed from the listeners properly.

so, keep those SecondaryAppWindowinstances. It can be in a container such as anstd::vector` or individual instances but you must keep these if you want to draw properly to the other displays.

Then add a destructor for the SecondaryAppWindow and remove the listeners there.
It should look like

SecondaryAppWindow::~SecondaryAppWindow()
{
  ofRemoveListener(window->events().draw, this, &SecondaryAppWindow::onFrameEvent);
  ofRemoveListener(window->events().keyPressed, this, &SecondaryAppWindow::onKeyboardInteraction);
  ofRemoveListener(window->events().keyReleased, this, &SecondaryAppWindow::onKeyboardInteraction); 
}

Let me know if this helps.

Thanks for having a look-

If I add the destructor as described, it gets invoked as soon as the app starts, and gives a null pointer exception. Here’s the code that I’ve got now creating the secondary windows:

  for (int i = 1; i < appSettings.monitors.size(); i++) {
    SecondaryAppWindow w;
    w.setup(mainApp, mainWindow, appSettings.monitors[i], i);  
    mainApp->secondaryWindows.push_back(w);
  }

For now I’ve sidestepped the problem by creating 10 almost identical draw calls like this, and using the same information about the number of attached monitors to create the corresponding number of FBOs with one for each window:

void Screensaver::drawWindow6(ofEventArgs& args) {
  if (windowFBOs[6].isAllocated()) {
    windowFBOs[6].draw(0, 0);
  }
}

void Screensaver::drawWindow7(ofEventArgs& args) {
  if (windowFBOs[7].isAllocated()) {
    windowFBOs[7].draw(0, 0);
  }
}

//etc

and having each window register a different one based on the id it receives, which isn’t ideal, but it does mean I can get something working. Still, any additional help you can give to get to the bottom of this would be appreciated!

Thats the problem! you are creating a SecondaryAppWindow inside the for loop, which gets destroyed as soon as the current loop iteration finishes.
So what you need to do is to make use shared_ptr<SecondaryAppWindow> or unique_ptr<SecondaryAppWindow> instead of regular instances.
Thus you need to define mainApp’s secondaryWindows as

vector< shared_ptr<SecondaryAppWindow> >  secondaryWindows;

the you create these as follows.

  for (int i = 1; i < appSettings.monitors.size(); i++) {
    mainApp->secondaryWindows.push_back(make_shared<    SecondaryAppWindow>());
    mainApp->secondaryWindows.back()->setup(mainApp, mainWindow, appSettings.monitors[i], i);  
  }

Ah, that makes sense. Will give it a try after work and let you know.

Yep, that’s got it, thanks again! Good old C++.