How to change main sketch display?

I'm using this modified main method to run my main sketch on a second display in 3.0a5. The sketch doesnt run because my sketch path is lost, I tried exporting the sketch and it didnt work. Any suggestions?

static final void main(final String[] args) {
  final String sketch = Thread.currentThread().getStackTrace()[1].getClassName();
  final String[] monitor = { "--display=1", "--present" };
  final String[] params = concat(append(monitor, sketch), args);

  PApplet.main(params);
}

Answers

  • edited February 2016

    I've warned about that in your previous post:
    https://forum.Processing.org/two/discussion/comment/60819/#Comment_60819

    It works when run as an exported app.
    Another option is not use main() and go w/ runSketch() in setup().
    Besides "--display=" & "--present", pass "--sketch-path" to it.

  • Yes, I exported the sketch and it still didnt work. Could I pass the sketch path in the main()?

    final String[] monitor = { "--display=1", "--present","--sketch-path" };
    

    I really prefer not to use runSketch() because that way, I'd have to split my code into a main sketch and two projector sketches. One projector sketch for the live music visuals im making and one for the controlP5 buttons and knobs.

    I'm having trouble sharing variables and objects between these when I use the runSketch() method. I keep getting "cant make static reference to non-static value".

    Sorry this turned out to be such a lengthy discussion.

  • edited February 2016

    Could I pass the sketchPath in the main()?

    Unfortunately not! When we're using the PDE (Processing's IDE), that is the 1 responsible to pass "--sketch-path=" to our sketches.
    However when we hack the static final void main(final String[] args) {, the PDE somehow decides not to! X(
    As I had mentioned before, only when we run the exported app, outside the PDE, we got sketchPath back while hacking main().

    ... I'd have to split my code into a main sketch and two projector sketches.

    That sentence above is ambiguous and doesn't match what you had already told me before! [-(
    I remember you've said you had 1 external projector. Plus 1 main monitor.
    Is that still correct or it is now 1 monitor + 2 external projectors? ~:>
    Recall that every sketch automatically got 1 canvas window already. And each windows can target 1 display only.

    I really prefer not to use runSketch()...

    I've already told you there's no other way. Unless you know some hard hack into Processing's innards to change the chosen initial display on-the-fly. :-\"
    That is, each target display demands 1 PApplet instance.

    As said, we already got 1 free when we start a sketch. By default it's gonna target our main monitor.
    Therefore, for every other monitor(s) &/or projector(s), we're gonna need 1 extra PApplet.

    The most flexible way to ignite some PApplet instance is via runSketch().
    We can even pass to them many args like "--present", "--display=", "--location=", "--sketch-path=", etc, as you already know:

    final String[] projectorArgs = {
      "--display=1", 
      "--present", 
      "location=" + width/2 + "," + 100, 
      "--sketch-path=" + sketchPath()
    };
    

    Another example based on: https://forum.Processing.org/two/discussion/comment/46184/#Comment_46184

    /**
     * Multi-Monitor Sketch II (v1.0)
     * by GoToLoop (2016/Feb/11)
     *
     * forum.Processing.org/two/discussion/14835/
     * how-to-change-main-sketch-display
     *
     * forum.Processing.org/two/discussion/11304/
     * multiple-monitors-primary-dragable-secondary-fullscreen
     *
     * forum.Processing.org/two/discussion/10937/multiple-sketches
     */
    
    final Projector projector = new Projector(); // instantiate
    
    void setup() {
      size(300, 200);
      smooth(4);
      frameRate(2);
    
      final String[] projectorArgs = {
        "--display=1", 
        "--present", 
        "location=" + width/2 + "," + 100, 
        "--sketch-path=" + sketchPath, 
        ""
      };
    
      runSketch(projectorArgs, projector); // ignite
      projector.frame.hide(); // hide
      projector.noLoop(); // pause
    }
    
    void draw() {
      background((color) random(#000000));
    }
    
    void mousePressed() {
      final boolean visible = projector.frame.isVisible();
      projector.frame.setVisible(!visible);
    
      if (visible)  projector.noLoop();
      else          projector.loop();
    }
    
    public class Projector extends PApplet {
      void setup() {
        size(displayWidth, displayHeight);
        smooth(4);
        frameRate(10);
    
        println(sketchPath);
      }
    
      void draw() {
        background((color) random(#000000));
      }
    }
    
  • What I wanted to do before was to have a single sketch switch between monitors when a certain key was pressed. But now I think its easier to use the runSketch() method.

    So just to make sure I've understood this right, I should have 1 main sketch doing all the calculations and processing all the objects (I'll keep it hidden) and 2 sketches; 1 a projector sketch on the second monitor and the other is my controlP5 sketch?

    This would work perfectly but everytime the controlP5 knobs / sliders change, the objects used by my projector sketch will need to change. I'm thinking of instantiating the projector sketch each time this happens.

    Is there an easier way of doing this? You mentioned using a PGraphics object but will a projector sketch be able to access this object from the main sketch?

  • edited February 2016

    I should have 1 main sketch doing all the calculations and processing all the objects (I'll keep it hidden) and 2 sketches; 1 a projector sketch on the second monitor and the other is my controlP5 sketch?

    • You only need as many instances of PApplet as your number of displays.
    • According from what you're saying above, you've got 1 monitor & 1 external projector, right?
    • Therefore you just need 1 extra PApplet. The main sketch is already a PApplet.
    • About which of those 2 instances need to do the actual work, you can pretty much dump everything in 1 PApplet while the other does nothing!
    • Obviously that'd be a performance waste, given each PApplet got its own Thread & PGraphics canvas.
    • The ideal is split the job between those 2. But you can always prefer easy over speed.

    I'm thinking of instantiating the projector sketch each time this happens.

    • It's doable but... why re-creating a PApplet over & over when we can do it once only within setup()? :-\"
    • Not much different than loading image, sound & video in setup() rather than in draw() all the time.

    You mentioned using a PGraphics object but will a projector sketch be able to access this object from the main sketch?

    • Yes, that was the approach I've tipped to ya back then.
    • By projector you mean the PApplet class targeting it.
    • As you know there are 2 methods that ignite a PApplet class:
      PApplet.main() & PApplet.runSketch().
    • PApplet.main() can only ignite PApplet public static nested or top classes.
    • While PApplet.runSketch() doesn't care about that at all! \m/
    • When a nested class is inner (non-static), it can access every member from its enclosing class.
    • In my sample sketch above, Projector is an inner class and can access any field & call any method from the top sketch. :-bd
    • However that is considered bad programming b/c it breaks OOP encapsulation.
    • Most formal "correct" way for Projector to access, let's say some PGraphics field from top sketch, is by requesting it via its constructor. O:-)

    /**
     * Multi-Monitor Sketch II (v2.0)
     * by GoToLoop (2016/Feb/11)
     *
     * forum.Processing.org/two/discussion/14835/
     * how-to-change-main-sketch-display
     *
     * forum.Processing.org/two/discussion/11304/
     * multiple-monitors-primary-dragable-secondary-fullscreen
     *
     * forum.Processing.org/two/discussion/10937/multiple-sketches
     */
    
    PGraphics sharedPg;
    Projector projector;
    
    void setup() {
      size(300, 200);
      smooth(4);
      frameRate(2);
    
      sharedPg = createGraphics(width, height, JAVA2D);
      sharedPg.smooth(4);
      sharedPg.beginDraw();
      sharedPg.endDraw();
    
      final String[] projectorArgs = {
        "--display=1", 
        "--present", 
        "--sketch-path=" + sketchPath, 
        ""
      };
    
      projector = new Projector(sharedPg); // instantiate
      runSketch(projectorArgs, projector); // ignite
      projector.frame.hide(); // hide
      projector.noLoop(); // pause
    }
    
    void draw() {
      synchronized (sharedPg) {
        sharedPg.beginDraw();
        sharedPg.background((color) random(#000000));
        sharedPg.endDraw();
      }
    
      background(sharedPg);
    }
    
    void mousePressed() {
      final boolean visible = projector.frame.isVisible();
    
      if (visible)  projector.noLoop();
      else          projector.loop();
    
      projector.frame.setVisible(!visible);
    }
    
    public static class Projector extends PApplet {
      final PGraphics pg;
    
      Projector(PGraphics graph) {
        pg = graph;
      }
    
      void setup() {
        size(displayWidth, displayHeight);
        smooth(4);
        frameRate(10);
      }
    
      void draw() {
        synchronized (pg) {
          image(pg, 0, 0, width, height);
        }
      }
    }
    
  • Awesome. What is the role of synchronized()?

  • Just some precaution. I don't think it would be a good idea to let the other PApplet use image() at the same time the PGraphics is being updated from the main PApplet. :-\"

  • I update PGraphics in multiple places. Do I just use synchronized() as above in those places?

  • If you don't see any problems, no need for **synchronized ()` I guess.
    If used, they're just to protect the update part from the display part.

  • edited February 2016

    I can compile my code, but the projector sketch is not showing anything. I'm wondering if there is something I'm missing.

    import ddf.minim.*;
    import ddf.minim.analysis.*;
    import processing.opengl.*;
    import com.hamoid.*;
    
    import java.awt.Frame;
    import java.awt.BorderLayout;
    import java.awt.GraphicsEnvironment;
    import java.awt.GraphicsDevice;
    import java.awt.HeadlessException;
    import controlP5.*;
    
    PGraphics pg;
    
    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice[] devices = env.getScreenDevices();
    int numberofScreens = devices.length;  
    
    //Creek is the object for the river, meadow is the object containing
    //an array of the object Grass. 
    //Ground is an array of green pixels that forms the ground below
    Creek creek;
    
    int numPixCreek;
    
    float h1, h2;
    
    PShader pointShader;
    
    Projector projector;
    
    int scrWid, scrHei;
    
    void setup()  {
      size(400, 500,P2D);
    
      scrWid = devices[0].getDefaultConfiguration().getBounds().width;
      scrHei = devices[0].getDefaultConfiguration().getBounds().height;
    
      pg = createGraphics(scrWid, scrHei, OPENGL);
    
      pointShader = loadShader("pointfrag.glsl","pointvert.glsl");
    
      numPixCreek = 100;
    
      fill(150);
    
      //Width X Depth
      creek = new Creek(numPixCreek/2,numPixCreek, 500 ,1000);
    
      color(0,255,0);
    
      h1 = random((scrHei/2) - (scrHei/16), 0);
      h2 = h1 + random(-1,1)*random(0,scrHei/128);
    
      final String[] projectorArgs = {
        "--display=1", 
        "--present", 
        "--sketch-path=" + sketchPath, 
        ""
      };
    
      projector = new Projector(pg); // instantiate
      runSketch(projectorArgs, projector); // ignite
    }
    
    void draw()  {
    
      pg.shader(pointShader,POINTS);
    
      //Move the camera position
      pg.camera(20,20,20,20,20,0,0,1,0);
    
      pg.lights();
    
      pg.rotateY(25.5);
      pg.rotateX(12.33);
    
      pg.pushMatrix();
      //translate((width-10*creek.nX)/2,height/2,-5*creek.nZ);  
      pg.translate((scrWid)/2,scrHei/2,-1/2);  
      creek.display(pg);
      pg.popMatrix();
    }
    
    public class Projector extends PApplet {
      final PGraphics page;
    
      Projector(PGraphics graph) {
        page = graph;
      }
    
      void setup() {
        size(displayWidth, displayHeight);
        background(0);
      }
    
      void draw() {
        image(page, 0, 0, width, height);
      }
    }
    
    class Creek
    {    
      Creek(int numHor, int numDep, int wid, int dep)
      {    
      }
    
      void display(PGraphics p)
      {     
        p.beginDraw();
        for(int x = 0; x < 5; x+= 1)
        {
          for(int z = 0; z < 5; z+= 1)
          {
            //Some color randomization
            p.stroke(random(18,165),random(130,205),255);    
    
            p.point(x, z*x, z);
    
          }
        }
        p.endDraw();
      }
    }
    
  • edited February 2016

    Oh, you've got 2 main problems there:

    1. Normally only 1 PApplet can have OpenGL-based renderers.
    2. Only an OpenGL-based renderer canvas can manipulate & display OpenGL-based PGraphics.

    Your currently situation is main PApplet is using a P2D renderer canvas, which is OpenGL-based.
    While the Projector is using JAVA2D renderer canvas, which isn't OpenGL-based.

    Therefore the latter can't use the shared P2D PGraphics, b/c its canvas is JAVA2D.
    But at the same time you can't change it from JAVA2D to some other OpenGL-based renderer.
    B/c that would crash the whole sketch! :-SS

    So after all, how could such circular situation be ever fixed?
    My idea is share a PImage rather than a PGraphics.
    We just need to use arrayCopy() from canvas' pixels[] to sharedImg's own pixels[]. *-:)

    https://Processing.org/reference/arrayCopy_.html
    https://Processing.org/reference/pixels.html

    /**
     * Multi-Monitor Sketch III (v1.0)
     * by GoToLoop (2016/Feb/16)
     *
     * forum.Processing.org/two/discussion/14835/
     * how-to-change-main-sketch-display
     *
     * forum.Processing.org/two/discussion/11304/
     * multiple-monitors-primary-dragable-secondary-fullscreen
     *
     * forum.Processing.org/two/discussion/10937/multiple-sketches
     */
    
    PImage sharedImg;
    Projector projector;
    
    void setup() {
      size(300, 200, P2D);
      smooth(4);
      frameRate(2);
    
      sharedImg = createImage(width, height, RGB);
    
      final String[] projectorArgs = {
        "--display=1", 
        "--present", 
        "--sketch-path=" + sketchPath, 
        ""
      };
    
      projector = new Projector(sharedImg); // instantiate
      runSketch(projectorArgs, projector); // ignite
      projector.frame.hide(); // hide
      projector.noLoop(); // pause
    }
    
    void draw() {
      background((color) random(#000000));
    
      loadPixels();
      synchronized (sharedImg) {
        arrayCopy(pixels, sharedImg.pixels);
        sharedImg.updatePixels();
      }
    }
    
    void mousePressed() {
      final boolean visible = projector.frame.isVisible();
    
      if (visible)  projector.noLoop();
      else          projector.loop();
    
      projector.frame.setVisible(!visible);
    }
    
    public static class Projector extends PApplet {
      final PImage bg;
    
      Projector(PImage img) {
        bg = img;
      }
    
      void setup() {
        size(displayWidth, displayHeight);
        smooth(4);
        frameRate(10);
      }
    
      void draw() {
        synchronized (bg) {
          image(bg, 0, 0, width, height);
        }
      }
    }
    
Sign In or Register to comment.