Native window access and inset handling in Processing JAVA2D and P2D

Hello folks!

This is an exploration of native window access in P2D/P3D and JAVA2D.
In my Windows 10 environment, I was seeing offsets, and I was compelled to understand why.

And now I do!

This is an advanced topic and Chat GPT was used to support this research.

P2D Code
// Author: GLV
// Date: 2026-04-14
// ChatGPT supported.

// Description: 
// This is an exploration of native window access in P2D/P3D, 
// where the underlying GLWindow lets us read the window insets.

// This is displayed in console:
// The sketch has been resized from 1920x1200 to 1920x1181 by the operating system.
// This happened outside Processing, and may be a limitation of the OS or window manager.

import com.jogamp.newt.opengl.GLWindow;

void settings()
  {
  int w = displayWidth; 
  int h = displayHeight;
    
  size(w, h, P2D);   // or P3D
  }

void setup()
  {
  GLWindow glw = (GLWindow) surface.getNative();
  
  int left = glw.getInsets().getLeftWidth();
  int right = glw.getInsets().getRightWidth();
  int top = glw.getInsets().getTopHeight();
  int bottom = glw.getInsets().getBottomHeight();
  println("Insets L/T/R/B = " + left + " / " + top + " / " + right + " / " + bottom);  
  
  // Give these a try:
  //surface.setVisible(true);         // Show (true) or hide (false) the window
  //surface.setLocation(0, 0);        // Set desired window position (0, 0); frame insets will shift the sketch area.
  //surface.setLocation(-in.left, 0); // Offsets the left frame border so the sketch area starts at the desired position.
  //surface.setResizable(true);       // Lets the window be resized or maximized
  }

void draw()
  {
  fill(random(64, 128+64));  
  circle(random(width), random(height), 20);    
  }
JAVA2D Code

// Author: GLV
// Date: 2026-04-14
// ChatGPT support

// Description: 
// This is an exploration of native window access in JAVA2D, 
// where the underlying AWT Frame lets us read the window insets.

import processing.awt.PSurfaceAWT;
import java.awt.Insets;

void settings()
  {
  int w = displayWidth; 
  int h = displayHeight;
  println(w, h);
    
  size(w, h, JAVA2D);
  }

void setup()
  {
  PSurfaceAWT.SmoothCanvas canvas = (PSurfaceAWT.SmoothCanvas) surface.getNative();
  Insets in = canvas.getFrame().getInsets();
  
  println("Insets L/T/R/B = " + in.left + " / " + in.top + " / " + in.right + " / " + in.bottom);  

  // Give these a try:
  //surface.setVisible(true);         // Show (true) or hide (false) the window
  //surface.setLocation(0, 0);        // Set desired window position (0, 0); frame insets will shift the sketch area.
  //surface.setLocation(-in.left, 0); // Offsets the left frame border so the sketch area starts at the desired position.
  //surface.setResizable(true);       // Lets the window be resized or maximized
  }

void draw()
  {
  fill(random(64, 128+64));  
  circle(random(width), random(height), 20);    
  }

Insets:

Insets L/T/R/B = 8 / 31 / 8 / 8

There is more going on here than just Java calling getInsets(). On my Windows system, the returned inset values also line up exactly with the Win32 metrics documented here:

A lot of details sit between those layers.
This was certainly an adventure.

I will post an update later… and now is later and I added to post below.

That was fun!

ChatGPT research:

Summary

For transparency:
ChatGPT supported my research, analysis, wording, and drafting of this test, while the direction, review, and interpretation were guided by me.
It is subject to scrutiny and I have not included the 800+ word research which includes references.
This was not a simple task and took a lot of iterations to get to this point.

This test compares Frame.getInsets() in setup() and again in the first draw(), then compares both results to the corresponding Win32 metrics returned by GetSystemMetrics(). The goal is to check whether the inset values remain stable across both phases and whether they line up with the Windows frame, caption, and padded-border metrics. The output shows the actual Java/AWT inset values, the raw Windows metric values, the derived inset math, and whether the results match. It does not determine which internal AWT path produced the values, only whether the final values agree.

import processing.awt.PSurfaceAWT;
import java.awt.Insets;
import java.awt.Frame;
import com.sun.jna.platform.win32.User32;

Insets inSetup;
boolean reportedInDraw = false;

void settings()
  {
  size(600, 400, JAVA2D);
  }

void setup()
  {
  Frame frame = getFrameRef();
  inSetup = frame.getInsets();

  printInsetReport("setup()", inSetup);
  }

void draw()
  {
  background(240);

  if(reportedInDraw) return;

  Frame frame = getFrameRef();
  Insets inDraw = frame.getInsets();

  printInsetReport("first draw()", inDraw);

  println();
  println("setup() vs first draw() match = " +
    (inSetup.left == inDraw.left &&
     inSetup.top == inDraw.top &&
     inSetup.right == inDraw.right &&
     inSetup.bottom == inDraw.bottom));

  reportedInDraw = true;
  noLoop();
  }

Frame getFrameRef()
  {
  PSurfaceAWT.SmoothCanvas canvas = (PSurfaceAWT.SmoothCanvas) surface.getNative();
  return canvas.getFrame();
  }

void printInsetReport(String label, Insets in)
  {
  int cxFrame   = User32.INSTANCE.GetSystemMetrics(32); // SM_CXFRAME
  int cyFrame   = User32.INSTANCE.GetSystemMetrics(33); // SM_CYFRAME
  int cyCaption = User32.INSTANCE.GetSystemMetrics(4);  // SM_CYCAPTION
  int cxPadded  = User32.INSTANCE.GetSystemMetrics(92); // SM_CXPADDEDBORDER

  int leftFromMetrics   = cxFrame + cxPadded;
  int topFromMetrics    = cyFrame + cxPadded + cyCaption;
  int rightFromMetrics  = cxFrame + cxPadded;
  int bottomFromMetrics = cyFrame + cxPadded;

  println();
  println("=== " + label + " ===");
  println("Actual Insets L/T/R/B = " + in.left + " / " + in.top + " / " + in.right + " / " + in.bottom);
  println();

  println("Raw Windows metrics:");
  println("SM_CXFRAME        = " + cxFrame);
  println("SM_CYFRAME        = " + cyFrame);
  println("SM_CYCAPTION      = " + cyCaption);
  println("SM_CXPADDEDBORDER = " + cxPadded);
  println();

  println("Derived inset math:");
  println("left   = SM_CXFRAME + SM_CXPADDEDBORDER = " + cxFrame + " + " + cxPadded + " = " + leftFromMetrics);
  println("top    = SM_CYFRAME + SM_CXPADDEDBORDER + SM_CYCAPTION = " + cyFrame + " + " + cxPadded + " + " + cyCaption + " = " + topFromMetrics);
  println("right  = SM_CXFRAME + SM_CXPADDEDBORDER = " + cxFrame + " + " + cxPadded + " = " + rightFromMetrics);
  println("bottom = SM_CYFRAME + SM_CXPADDEDBORDER = " + cyFrame + " + " + cxPadded + " = " + bottomFromMetrics);
  println();

  println("Derived Insets L/T/R/B = " + leftFromMetrics + " / " + topFromMetrics + " / " + rightFromMetrics + " / " + bottomFromMetrics);

  println("Metric match = " +
    (in.left == leftFromMetrics &&
     in.top == topFromMetrics &&
     in.right == rightFromMetrics &&
     in.bottom == bottomFromMetrics));
  }

Console output on y W10 PC:

0 launchVirtualMachine()
Free port allocated: 52159
freePort: 52159
Sketch runner port: 52159

=== setup() ===
Actual Insets L/T/R/B = 8 / 31 / 8 / 8

Raw Windows metrics:
SM_CXFRAME = 4
SM_CYFRAME = 4
SM_CYCAPTION = 23
SM_CXPADDEDBORDER = 4

Derived inset math:
left = SM_CXFRAME + SM_CXPADDEDBORDER = 4 + 4 = 8
top = SM_CYFRAME + SM_CXPADDEDBORDER + SM_CYCAPTION = 4 + 4 + 23 = 31
right = SM_CXFRAME + SM_CXPADDEDBORDER = 4 + 4 = 8
bottom = SM_CYFRAME + SM_CXPADDEDBORDER = 4 + 4 = 8

Derived Insets L/T/R/B = 8 / 31 / 8 / 8
Metric match = true

=== first draw() ===
Actual Insets L/T/R/B = 8 / 31 / 8 / 8

Raw Windows metrics:
SM_CXFRAME = 4
SM_CYFRAME = 4
SM_CYCAPTION = 23
SM_CXPADDEDBORDER = 4

Derived inset math:
left = SM_CXFRAME + SM_CXPADDEDBORDER = 4 + 4 = 8
top = SM_CYFRAME + SM_CXPADDEDBORDER + SM_CYCAPTION = 4 + 4 + 23 = 31
right = SM_CXFRAME + SM_CXPADDEDBORDER = 4 + 4 = 8
bottom = SM_CYFRAME + SM_CXPADDEDBORDER = 4 + 4 = 8

Derived Insets L/T/R/B = 8 / 31 / 8 / 8
Metric match = true

setup() vs first draw() match = true

The first part was a modified Processing to assign a free port.

:)