Get the current instance of PApplet from a library

Sorry for reviving this topic but I might have found an interesting aproach that might work:

I have the following idea:
You can get all active windows from an active thread so you might be able to access the asociated JFrame. From there you could get the SmoothCanvas.
In the creation of the SmoothCanvas the sketch as a field.
Here is the part I didn’t manage:
SmoothCanvas is a nested class of PSurfaceAWT. And the field itself is a part of the superclass of PSurfaceAWT and is protected.
After seeing reflection isn’t very helpful I tried the folowing:
MainSketch.pde

void setup() {
  javax.swing.JFrame  mainFrame=(javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)this.getSurface().getNative()).getFrame();
  println(GetPApplet.get(mainFrame.getContentPane().getComponents()[0]));
}
void draw() {
}

GetPApplet.java

package processing.core;
import java.awt.*;
import processing.awt.*;
import javax.swing.*;
import processing.core.*;
public class GetPApplet{

  public static PApplet get(java.awt.Component c) {
    processing.awt.PSurfaceAWT.SmoothCanvas sc=(processing.awt.PSurfaceAWT.SmoothCanvas) c;
    PApplet f=sc.sketch;
    return f;
  }
}

However trying it gives me: sketch cannot be resolved or is not a field

So I’m looking for a salution to one of the following problems:

  • Get the PSurfaceAWT from the window
  • Get the PSurfaceAWT from the SmoothCanvas
  • Access a field of the class from within a instance of a nested class

I have written a class that does something simular to finding the instance of the current PApplet. It replaces the PApplet with a copy that has the same properties as the original one disables the first one and returns the new one:

PAppletFinderTest.pde

int test=5;
void setup() {
  PApplet newpapplet=PAppletFinder.force();
  println(newpapplet,this);
  ((PAppletFinderTest)newpapplet).test=1;
  println(test);
}

PAppletFinder.java

import processing.awt.PSurfaceAWT;
import processing.core.PApplet;


import java.util.*;
import javax.swing.JFrame;
import java.lang.reflect.*;
public class PAppletFinder {
  public static boolean forceAllowed=true;
  public static volatile boolean newThread=false;
  public static volatile PApplet genPApplet=null;
  public static void deleteAllWindows() {
    java.awt.Window win[]=java.awt.Window.getWindows();
        for(int i=0;i<win.length;i++) win[i].dispose();
  }
  public static PApplet retrieve() {
    return genPApplet;
  }
  public static Class<?> getSketchClass() {
      ClassLoader orgcl=Thread.currentThread().getContextClassLoader();
      Class<?> searched=null;
      try {
        Thread.currentThread().setContextClassLoader(PAppletFinder.class.getClassLoader());
        Field f=ClassLoader.class.getDeclaredField("classes");
        f.setAccessible(true);
        Vector<Class> classes=(Vector<Class>)f.get(PAppletFinder.class.getClassLoader());
        for (Class c:classes) {
          //println(c);
          if (c.getSuperclass()==PApplet.class) searched=c;
        }
        //println(searched);
      }
      catch(Exception e) {
        e.printStackTrace();
      }
      finally {
        Thread.currentThread().setContextClassLoader(orgcl);
      }
      return searched;
    }
  public static PApplet force() {
      if (!newThread) {
      if(!forceAllowed) throw new ForceNotAllowedException("Restarting the sketch isn't allowed");
        PApplet newSketch=null;
        try {
        java.awt.Window win[]=java.awt.Window.getWindows();
        int xwin = 0,ywin=0;
        if(win.length>0) {
          JFrame frame_win=(JFrame)win[win.length-1];
          xwin=frame_win.getLocation().x;
          ywin=frame_win.getLocation().y;
        }
        for(int i=0;i<win.length;i++) win[i].dispose();
          Class<?> c=getSketchClass();
          newSketch=(PApplet) c.newInstance();
          newThread=true;
          PApplet.runSketch(new String[]{newSketch.toString()}, newSketch);
          genPApplet=newSketch;
          if(genPApplet.sketchRenderer().matches(PConstants.JAVA2D))((PSurfaceAWT.SmoothCanvas)(genPApplet.getSurface().getNative())).getFrame().setLocation(xwin, ywin);
          while(newThread){}
        }
        catch(Exception e) {
          e.printStackTrace();
        }
        return newSketch;
      }else return genPApplet;
    }
  static class ForceNotAllowedException extends RuntimeException{
    public ForceNotAllowedException() {
      super();
    }
    public ForceNotAllowedException(String message) {
      super(message);
    }
  }
static{find();}
}

I’ve done it!

import processing.awt.*;
import processing.core.*;

import javax.swing.*;
import java.awt.*;
import java.lang.reflect.*;
/**This class finds the active PApplet (Processing program)
 * @author NumericPrime*/
public class PAppletFinder{
/**This class takes the contents of the window and pieces together the PApplet
 * @return PApplet of the current sketch.
 * @param c contents of the Processing window (must be a SmoothCanvas)*/
public static PApplet get(Component c) {
  PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
  try {
	PSurfaceAWT psa=(PSurfaceAWT) get(sc,"this$0",sc.getClass());
    PApplet prg=(PApplet) get((PSurfaceNone)psa,"sketch",PSurfaceNone.class);
    return prg;
  }
  catch(Exception e) {
    e.printStackTrace();
  }
  return null;
}

public static PApplet foundPapplet=null;
/**The main method to be used when using the PAppletFinder*/
public static PApplet find() {
	if(foundPapplet==null) foundPapplet=findFromWindow();
	return foundPapplet;
}

/**This looks out for windows and gives the contents of the right one to the get method
 * @return PApplet of the current sketch.*/
public static PApplet findFromWindow() {
  JFrame mainWindow=null;
  java.awt.Window win[]=java.awt.Window.getWindows();
  for (int i=0; i<win.length&&mainWindow==null; i++) if (win[i] instanceof JFrame) mainWindow=(JFrame) win[i];

  Component c=mainWindow.getContentPane().getComponents()[0];
  return get(c);
}
/**This is used to get the this$0 field of the SmoothCanvas
 * Directly searching for the field didn't work. However for some reason
 * looking through all the fields does the trick.
 * @return Object value of a field
 * @param j the Object from which the value is taken
 * @param target name of the field*/
public static Object get(Object j,String target,Class<?> ref) {
	Field[] field_=ref.getDeclaredFields();
	for(int i=0;i<field_.length;i++) {
		field_[i].setAccessible(true);
		try {
			if(field_[i].getName().equals(target)) return field_[i].get(j);
			} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
return null;
}
}

(I will expand upon this to make it more suitable for using it in a library) Once I found out in wich catagory to put it I will post the done code.

Well done on finding a way to do this. :grinning:

Your solution works well for sketches using JAVA2D but not in P2D and P3D modes (at least in Processing 3.5.4) I don’t know if this is important to you :question:

3 Likes

I could scout the processing source code to find out more about it. If its just another component that is used I can adjust my code.

Ok I looked in the source code. My code has the following problem:
When using any other other renderer than JAVA2D Processing won’t use JFrames anymore but rather the OpenGL windows it seems.
The reason my code works in the first place is that I can look up all active windows and find the Processing window.

However it seems that the windows generated by P2D and P3D aren’t instances of java.awt.Window so aren’t tracked by getWindows() . This makes it way harder to get a point from wich to try and get the PApplet.

I might get something to work if I can get a custom class to load before the window is created.

This seems an awful lot of work to avoid the sketch providing the PApplet object in the first place.

4 Likes

Never mind everyone… I did just ignore the most obvious possibility:

PAppletFinder.java

import javax.swing.*;
import java.awt.*;
import java.lang.reflect.*;
/**This class finds the active PApplet (Processing program)
 * @author NumericPrime*/
public class PAppletFinder {
  public static PApplet findPApplet() {
    java.awt.Window win[]=java.awt.Window.getWindows();
    return foundPapplet=(PApplet) (get(win[0], "this$0", win[0].getClass()));
  }
  public static PApplet foundPapplet=null;
  /**The main method to be used when using the PAppletFinder*/
  public static PApplet find() {
    if (foundPapplet==null) findPApplet();
    return foundPapplet;
  }
  /**This is used to get the this$0 field of the SmoothCanvas
   * Directly searching for the field didn't work. However for some reason
   * looking through all the fields does the trick.
   * @return Object value of a field
   * @param j the Object from which the value is taken
   * @param target name of the field*/
  public static Object get(Object j, String target, Class<?> ref) {
    Field[] field_=ref.getDeclaredFields();
    for (int i=0; i<field_.length; i++) {
      field_[i].setAccessible(true);
      try {
        if (field_[i].getName().equals(target)) return field_[i].get(j);
      } 
      catch (IllegalArgumentException e) {
        e.printStackTrace();
      } 
      catch (IllegalAccessException e) {
        e.printStackTrace();
      }
    }
    return null;
  }
}


mainSketch

void setup() {
  size(300,300,P3D);
  println(PAppletFinder.find());
  println(this);
}
void draw() {
}

Edit: Made the code PAppletFinder in a .java file one can use in a library

Link for old forum topics mentioning hidden field “this$0”

1 Like

Do you mean this one?
Problem with method shadowing - Processing 2.x and 3.x Forum

1 Like

Any of those 5 topics under tag “this$0”. :wink:

1 Like

Problem is for some reason this code doesn’t work:

import java.awt.*;
import java.lang.reflect.*;
void setup() {
  try {
    java.awt.Window w=java.awt.Window.getWindows()[0];
    Field f=w.getClass().getDeclaredField("this$0");
    f.setAccessible(true);
    PApplet found=(PApplet) f.get(w);
    println(found);
    println(this);
  }
  catch(Exception e) {
    e.printStackTrace();
  }
}

Edit: Never mind it works

1 Like

  • Hidden field this$0 points to the most immediate enclosing class.
  • For a class to have the hidden field this$0 it must be an inner class.
  • There are 2 types of inner classes:
    1. Non-static nested class
    2. Anonymous created class

  • A non-static nested class is any class that isn’t declared w/ keyword static & it’s defined inside another class.
  • In Processing all non-static classes defined inside a “.pde” file is by definition an inner class.
  • That’s b/c before our sketch is compiled all “.pde” files are concatenated as 1 “.java” file.
  • Then that whole content is wrapped up inside a PApplet subclass.
  • Your attempt above failed b/c it wasn’t inside a non-static class.
  • In my linked example the code is defined inside a non-static class called Inner.

  • Now for the 2nd type of inner class we have anonymous instantiation.
  • That special kind of instantiation happens when we instantiate a class or interface at the same time we add more stuff to it on-the-fly using curly braces {}.
  • This is a regular instantiation: List<Long> vecs = new ArrayList<>();
  • But even an empty {} at the end: List<Long> vecs = new ArrayList<>() {};
  • is enough to turn that into an anonymous instantiation, making that particular ArrayList object to have a hidden field this$0 pointing to the class that code is defined!
  • There’s a particular anonymous instantiation inside PApplet’s source code that creates a Frame object: frame = new Frame() {
  • A Frame is a subclass of Window, which you’ve smartly found out how to grab it via the static method Window.getWindows().
  • B/c it has been instantiated using curly braces {}, that Frame inherited a PApplet this$0 hidden reference!
  • However you should consider some rare edge cases which a sketch might create other Window objects.
  • So you should check the whole Window[] array until you find a this$0 that is an instanceof PApplet.
  • Unfortunately your hackish way of auto-discovering the sketch’s PApplet reference will be short-lived.
  • Processing 4 has commented out that whole Frame instantiation block!
  • So your workaround is stuck w/ previous Processing versions only:

1 Like

That is unfortunate.

I have 2 other ways that could work. Either the JFrame-approach that only works to JAVA2D
or another way would be to find out the class of the main PApplet and then replace the old one with a new one. The one problem is that I can’t close JOGL windows as something like Window.getWindows() doesn’t work. So it would just open a second window with the old one frozen.

I found out about the created frame by looking at the class of the Frame wich was

processing.core.PApplet$7

The $7 being a telltale sign of a annonymous inner class.
So checking if the classnames match processing.core.PApplet\\$\\d+
should suffice in that regard.

Do you know if processing 4 will have all the system libraries especially thoes derived from tools.jar?

If so I would have access to byte-code manipulation wich would be a last resort…

1 Like

Actually this is the 1st time I’ve ever searched for anything related to P4.
I still didn’t install P4. Awaiting for most of its bugs to be resolved.

2 Likes

I came up with another solution that uses another weakpoint specificy for when using JOGL.
processing/PSurfaceJOGL.java at master · processing/processing · GitHub
At this point a new Thread is dispatched and by checking all active threads you can find out that it has the name “Thread-3” from there you can get the Runnable
Thread.target. This is a inner class of PSurfaceJOGL so you can use this$0 to get it. And PSurfaceJOGL has a field called sketch. This can be combined with the solution for JAVA2D to cover all possibilities.
This solution probably will work with processing 4 with a few adjustments as processing 4 also dispatches the same thread with the one difference that lambdas are used.
processing4/PSurfaceJOGL.java at master · processing/processing · GitHub
Here is the code that worked in the end:

import processing.awt.*;
import processing.opengl.*;
import processing.core.*;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.lang.reflect.*;
/**This class finds the active PApplet (Processing program)*/
public class PAppletFinder {
  /**This class takes the contents of the window and pieces together the PApplet
   * @return PApplet of the current sketch.
   * @param c contents of the Processing window (must be a SmoothCanvas)*/
  public static PApplet get(Component c) {
    PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
    try {
      PSurfaceAWT psa=(PSurfaceAWT) get(sc, "this$0", sc.getClass());
      PApplet prg=(PApplet) get((PSurfaceNone)psa, "sketch", PSurfaceNone.class);
      return prg;
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    return null;
  }

  public static PApplet foundPapplet=null;
  /**The main method to be used when using the PAppletFinder*/
  public static PApplet find() {
    if (foundPapplet==null) foundPapplet=findFromWindow();
    if (foundPapplet==null) foundPapplet=fromThreads();
    return foundPapplet;
  }

  /**This looks out for windows and gives the contents of the right one to the get method
   * @return PApplet of the current sketch.*/
  public static PApplet findFromWindow() {
    JFrame mainWindow=null;
    java.awt.Window win[]=java.awt.Window.getWindows();
    for (int i=0; i<win.length&&mainWindow==null; i++) if (win[i] instanceof JFrame) {
      mainWindow=(JFrame) win[i];
      Component c=mainWindow.getContentPane().getComponents()[0];
      return get(c);
    }
    return null;
  }
  /**This is used to get the value of fields
   * @return Object value of a field
   * @param j the Object from which the value is taken
   * @param target name of the field*/
  public static Object get(Object j, String target, Class<?> ref) {
      try {
        Field f=ref.getDeclaredField(target);
        f.setAccessible(true);
        return f.get(j);
      } 
      catch (Exception e) {
        e.printStackTrace();
      } 
    return null;
  }
  /**When using P2D or P3D everything is built differently however there is a weakness to be found in PSurfaceJOGL as it dispatches a Thread using an annonymous inner class as Runnable*/
  public static PApplet fromThreads() {
    Set<Thread> threadSet=Thread.getAllStackTraces().keySet();
    //println(Thread.currentThread().getThreadGroup());
    for (Thread th : threadSet) if (th.getName().matches("Thread-3")) {
      //println(" ");
      //println("Thread: Class:"+th.getClass()+" Name:"+th.getName()+" Daemon:"+th.isDaemon());
      {
        try {
          //for (Field f : th.getClass().getDeclaredFields()) println(f);
          Field f=th.getClass().getDeclaredField("target");
          f.setAccessible(true);
          Object currinstance=null;
          currinstance=f.get(th);
          currinstance=getEnclosingInstance(currinstance);
          f=PSurfaceJOGL.class.getDeclaredField("sketch");
          f.setAccessible(true);
          return (PApplet) (currinstance=f.get(currinstance));
        }
        catch(Exception e) {
          System.err.println("An error occurred");
        }
      }
      //innerClassesLoaded(th);
    }
    return null;
  }

  public static Object getEnclosingInstance(Object o) {
    try {
      Class<?> classused=o.getClass();
      Field this0=classused.getDeclaredField("this$0");
      this0.setAccessible(true);
      return this0.get(o);
    }
    catch(Exception e) {
      return null;
    }
  }
}

An example-sketch using this would be:

void setup() {
  size(300,300,JAVA2D);
  println(this,PAppletFinder.find());
}
void draw() {
}

2 Likes

I had a friend run the code and it seems it doesn’t work for P2D and P3D in Processing 4.

The Problem is the following:
Java 17 made the .setAccessible methods for the java.lang.* classes not work anymore.
When I try to access a field of java.lang.Thread it won’t work anymore.
So I either try to find a new weak spot or try to understand the library for bypassing Project Jigsaw.

I’m currently analysing the permit-reflect library.

1 Like

Since Java 9 method setAccessible() also takes into consideration the module a reflected member belongs to:

There’s an --add-opens option for the Java’s command-line, but dunno how to add that to Processing:
OpenJDK.org/jeps/261#Breaking-encapsulation

2 Likes

The new part is that since Java 17 the java.lang package is also not open anymore and I need to reflect a field from the thread object.

I don’t think it is possible to add more commandline arguments to an already running JVM.

Currently I’m experimenting with byte-code although on Java 12. I’m currently trying to load a custom class into java.lang. If that works maybe I can trick java into giving me access to the target Field of Thread. (Edit: Ok it doesn’t work)

1 Like

This code might do the trick. It also works with processing 3 and this code is able to breach encapsulation. (As long as there is no SecurityManager in place)

PAppletFinder.java

import processing.awt.*;
import processing.opengl.*;
import processing.core.*;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.lang.reflect.*;

import sun.misc.*;
/**This class finds the active PApplet (Processing program)*/
public class PAppletFinder {
  /**Unsafe instance used*/
  public static Unsafe unsafe=null;
  /**Unsafe Field offset*/
  public static long fieldIndex=0;
  /**Target field of the thread*/
  public static Field threadTargField=null;
  /**Here the three Fields unsafe,fieldIndex, threadTargField are initalized*/
  static {
    try {
      Field f2=Unsafe.class.getDeclaredField("theUnsafe");
      f2.setAccessible(true);
      unsafe=(Unsafe) f2.get(null);
      System.out.println("Unsafe instance: "+unsafe.toString());
      threadTargField=Thread.class.getDeclaredField("target");
      fieldIndex=unsafe.objectFieldOffset(threadTargField);
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }

  /**All results will be saved here*/
  public static java.util.List<PApplet> allFoundPapplets=new LinkedList<PApplet>();
  /**This class takes the contents of the window and pieces together the PApplet
   * @return PApplet of the current sketch.
   * @param c contents of the Processing window (must be a SmoothCanvas)*/
  public static void get(Component c) {
    System.out.println("Processing Window content");
    PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
    try {
      PSurfaceAWT psa=(PSurfaceAWT) get(sc, "this$0", sc.getClass());
      PApplet prg=(PApplet) get((PSurfaceNone)psa, "sketch", PSurfaceNone.class);
      allFoundPapplets.add( prg);
    }
    catch(Exception e) {
      System.err.println("Something went wrong");
      e.printStackTrace();
    }
  }

  public static PApplet foundPapplet=null;
  /**The main method to be used when using the PAppletFinder*/
  public static void findAll() {
    findFromWindow();
    fromThreads();
    System.out.println("Detection done");
  }

  /**This looks out for windows and gives the contents of the right one to the get method
   * @return PApplet of the current sketch.*/
  public static void findFromWindow() {
    System.out.println("Searching Windows for instances");
    JFrame mainWindow=null;
    java.awt.Window win[]=java.awt.Window.getWindows();
    for (int i=0; i<win.length; i++) if (win[i] instanceof JFrame) {
      mainWindow=(JFrame) win[i];
      Component c=mainWindow.getContentPane().getComponents()[0];
      if (c instanceof PSurfaceAWT.SmoothCanvas) get(c);
    }
  }
  /**This is used to get the value of fields
   * @return Object value of a field
   * @param j the Object from which the value is taken
   * @param target name of the field*/
  public static Object get(Object j, String target, Class<?> ref) {
    try {
      Field f=ref.getDeclaredField(target);
      f.setAccessible(true);
      return f.get(j);
    } 
    catch (Exception e) {
      e.printStackTrace();
    } 
    return null;
  }
  /**When using P2D or P3D everything is built differently however there is a weakness to be found in PSurfaceJOGL as it dispatches a Thread using an annonymous inner class as Runnable*/
  public static void fromThreads() {
    System.out.println("Searching Threads for instances");
    //This fetches all threads
    Set<Thread> threadSet=Thread.getAllStackTraces().keySet();
    //It iterates upon
    for (Thread th : threadSet) {
    iteration:
      {
        try {
          //Field f=th.getClass().getDeclaredField("target");
          //f.setAccessible(true);
          Object currinstance=null;
          currinstance=unsafe.getObject(th,fieldIndex);
          if (!currinstance.getClass().toString().contains("PSurfaceJOGL$")) break iteration;
          System.out.println("Weak spot found! "+th.getName());
          currinstance=getEnclosingInstance(currinstance);
          Field f=PSurfaceJOGL.class.getDeclaredField("sketch");
          f.setAccessible(true);
          allFoundPapplets.add((PApplet) (currinstance=f.get(currinstance)));
          System.out.println("Detection successful");
        }
        catch(Exception e) 
        {
        catchBlock:
          {
            if (e instanceof NoSuchFieldException||e instanceof NullPointerException) {
              System.err.println("target not found "+th.getName());
              break catchBlock;
            }
            System.err.println("Something went wrong!");
            e.printStackTrace();
          }
        }
      }
    }
  }
  //This will try to get the enclosing instance of an object (this can also be a lambda)
  public static Object getEnclosingInstance(Object o) {
    //This code will work for Processing 3 (uses annonymous inner classes)
    try {
      Class<?> classused=o.getClass();
      Field this0=classused.getDeclaredField("this$0");
      this0.setAccessible(true);
      return this0.get(o);
    }
    catch(Exception e) {
      System.err.println("Can't find enclosing instance of Object trying to use Lambdas... (To be expected with Processing 4)");
    }
    //This code should work for Processing 4 (lambdas are used instead of inner classes, Lambdas use arg$1 instead this$0) 
    try {
      Class<?> classused=o.getClass();
      Field this0=classused.getDeclaredField("arg$1");
      this0.setAccessible(true);
      return this0.get(o);
    }
    catch(Exception e) {
      System.err.println("Detection Failed!");
    }
    return null;
  }
}

I did some minor changes to my code. Here is the version that is confirmed to work for P4:

PAppletFinder.java

import processing.awt.*;
import processing.opengl.*;
import processing.core.*;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.lang.reflect.*;

import sun.misc.*;
/**This class finds the active PApplet (Processing program)
 @author NumericPrime*/
public class PAppletFinder {
  /**Unsafe instance used*/
  public static Unsafe unsafe=null;
  /**Unsafe Field offset*/
  public static long fieldIndex=0;
  /**Target field of the thread*/
  public static Field threadTargField=null;
  /**Here the three Fields unsafe,fieldIndex, threadTargField are initalized*/
  static {
    try {
      Field f2=Unsafe.class.getDeclaredField("theUnsafe");
      f2.setAccessible(true);
      unsafe=(Unsafe) f2.get(null);
      //System.out.println("Unsafe instance: "+unsafe.toString());
      threadTargField=Thread.class.getDeclaredField("target");
      fieldIndex=unsafe.objectFieldOffset(threadTargField);
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  /**Getter-Method used to detect all PApplets. If the detection hasn't run yet this method will run it.
   @param refresh if true: redetects all PApplets
   @return all detected PApplets*/
  public static PApplet[] getPApplets(boolean refresh) {
    int allfpSize=0;
    if (refresh) allFoundPapplets.clear();
    if ((allfpSize=allFoundPapplets.size())==0) findAll();
    return allFoundPapplets.toArray(new PApplet[allfpSize]);
  }
  /**All results will be saved here*/
  public static java.util.List<PApplet> allFoundPapplets=new LinkedList<PApplet>();
  /**This class takes the contents of the window and pieces together the PApplet
   * @return PApplet of the current sketch.
   * @param c contents of the Processing window (must be a SmoothCanvas)*/
  public static void get(Component c) {
    //System.out.println("Processing Window content");
    PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
    try {
      //gets the PSurface for the PApplet
      PSurfaceAWT psa=(PSurfaceAWT) get(sc, "this$0", sc.getClass());
      //gets the PApplet
      PApplet prg=(PApplet) get((PSurfaceNone)psa, "sketch", PSurfaceNone.class);
      allFoundPapplets.add(prg);
    }
    catch(Exception e) {
      //System.err.println("Something went wrong");
      e.printStackTrace();
    }
  }
  /**This method tries to detect all PApplet used*/
  public static void findAll() {
    findFromWindow();
    fromThreads();
    //System.out.println("Detection done");
  }

  /**This looks out for processing-windows and gives the contents of the right one to the get method*/
  public static void findFromWindow() {
    //System.out.println("Searching Windows for instances");
    JFrame mainWindow=null;
    java.awt.Window win[]=java.awt.Window.getWindows();
    for (int i=0; i<win.length; i++) if (win[i] instanceof JFrame) {
      mainWindow=(JFrame) win[i];
      Component c=mainWindow.getContentPane().getComponents()[0];
      if (c instanceof PSurfaceAWT.SmoothCanvas) get(c);
    }
  }
  /**This is used to get the value of fields
   * @return Object value of a field
   * @param j the Object from which the value is taken
   * @param target name of the field
   * @param ref class the field is taken from*/
  public static Object get(Object j, String target, Class<?> ref) {
    try {
      Field f=ref.getDeclaredField(target);
      f.setAccessible(true);
      return f.get(j);
    } 
    catch (Exception e) {
      e.printStackTrace();
    } 
    return null;
  }
  /** When using P2D or P3D everything is built differently however there is a weakness to be found in 
    * PSurfaceJOGL as it dispatches a Thread using an annonymous inner class as Runnable*/
  public static void fromThreads() {
    //System.out.println("Searching Threads for instances");
    //This fetches all threads
    Set<Thread> threadSet=Thread.getAllStackTraces().keySet();
    //It iterates upon the threads to find ones dispatched by PSurfaceJOGL
    for (Thread th : threadSet) {
    iteration:
      {
        try {
          //Field f=th.getClass().getDeclaredField("target");
          //f.setAccessible(true);
          Object currinstance=null;
          
          //Here Unsafe is used to breach encapsulation of java.lang
          currinstance=unsafe.getObject(th, fieldIndex);
          if (!currinstance.getClass().toString().contains("PSurfaceJOGL$")) break iteration;
          //System.out.println("Weak spot found! "+th.getName());
          //gets the PSurfaceJOGL
          currinstance=getEnclosingInstance(currinstance);
          //gets the PApplet
          Field f=PSurfaceJOGL.class.getDeclaredField("sketch");
          f.setAccessible(true);
          allFoundPapplets.add((PApplet) (currinstance=f.get(currinstance)));
          //System.out.println("Detection successful");
        }
        catch(Exception e) 
        {
        catchBlock:
          {
            if (e instanceof NoSuchFieldException||e instanceof NullPointerException) {
              //System.err.println("target not found "+th.getName());
              //A NullPointerException may occur
              break catchBlock;
            }
            //System.err.println("Something went wrong!");
            e.printStackTrace();
          }
        }
      }
    }
  }
  //This will try to get the enclosing instance of an object (this can also be a lambda)
  public static Object getEnclosingInstance(Object o) {
    //This code will work for Processing 3 (uses annonymous inner classes)
    try {
      Class<?> classused=o.getClass();
      Field this0=classused.getDeclaredField("this$0");
      this0.setAccessible(true);
      return this0.get(o);
    }
    catch(Exception e) {
      //System.err.println("Can't find enclosing instance of Object, trying to use Lambdas... (To be expected with Processing 4)");
    }
    //This code should work for Processing 4 (lambdas are used instead of inner classes, Lambdas use arg$1 instead this$0) 
    try {
      Class<?> classused=o.getClass();
      Field this0=classused.getDeclaredField("arg$1");
      this0.setAccessible(true);
      return this0.get(o);
    }
    catch(Exception e) {
      //System.err.println("Detection Failed!");
    }
    return null;
  }
}

MainSketch.pde

void setup() {
  size(300,300,P3D);
  println(this,PAppletFinder.getPApplets(false)[0]);
}
void draw() {
}

Thanks for your help!

If anyone got questions about the done code feel free to ask.

2 Likes