Repository navigation
AWT Peer Implementation
JNode implements AWT peers using Swing components as a lightweight simulation of a desktop operating system, bypassing the need for a host OS windowing system.
Unlike traditional JVMs that delegate UI rendering to the host OS (X11, Windows), JNode implements the entire peer layer from scratch. The peer implementation uses Swing components as an internal rendering backend, creating a simulated desktop environment that draws directly to the framebuffer.
| Class | Location | Purpose |
|---|---|---|
JNodeToolkit |
gui/src/awt/org/jnode/awt/JNodeToolkit.java |
Base toolkit, bridges AWT to framebuffer |
SwingToolkit |
gui/src/awt/org/jnode/awt/swingpeers/SwingToolkit.java |
Creates all peer instances using Swing |
JNodeGraphicsEnvironment |
gui/src/awt/org/jnode/awt/JNodeGraphicsEnvironment.java |
Manages screen devices and fonts |
AWTPlugin |
gui/src/awt/org/jnode/awt/AWTPlugin.java |
Plugin lifecycle management |
DesktopFrame |
gui/src/awt/org/jnode/awt/swingpeers/DesktopFrame.java |
Root window (the desktop) |
DesktopFramePeer |
gui/src/awt/org/jnode/awt/swingpeers/DesktopFramePeer.java |
Peer for desktop |
JNodeEventQueue |
gui/src/awt/org/jnode/awt/JNodeEventQueue.java |
Event dispatch |
KeyboardHandler |
gui/src/awt/org/jnode/awt/KeyboardHandler.java |
Raw keyboard to AWT events |
MouseHandler |
gui/src/awt/org/jnode/awt/MouseHandler.java |
Mouse input handling |
Boot → AWTPlugin.startPlugin()
→ VMImageUtils.setAPI(new VMImageAPIImpl())
→ Application calls Toolkit.getDefaultToolkit()
→ JNodeToolkit.incRefCount() initializes graphics mode
→ JNodeToolkit.onInitialize() (abstract, implemented by SwingToolkit)
→ SwingToolkit creates DesktopFrame
java.awt.Toolkit
└── gnu.java.awt.ClasspathToolkit (GNU Classpath)
└── org.jnode.awt.JNodeToolkit (abstract base)
└── org.jnode.awt.swingpeers.SwingToolkit
When an AWT component is created, the toolkit creates a peer:
// Example from SwingToolkit.java
protected ButtonPeer createButton(Button target) {
return new SwingButtonPeer(this, target);
}Each peer wraps a Swing component that handles rendering. The peer maps between AWT and Swing coordinate systems and event models.
JNode connects to video hardware through the Video-Driver-Architecture:
-
JNodeToolkitqueriesDeviceManagerforFrameBufferAPIimplementations - Requests ownership of the framebuffer via
api.requestOwnership(this) - Opens a
Surfacefor drawing:api.open(config.getConfig()) - All painting operations write directly to video memory
The DesktopFrame is the root of the component hierarchy:
// From SwingToolkit.onInitialize()
desktopFrame = new DesktopFrame(getScreenSize());
desktopFrame.show();
desktopFrame.getContentPane().repaint();All other AWT Frames are rendered as JInternalFrames inside this desktop's JDesktopPane.
Input Hardware → KeyboardHandler/MouseHandler
→ JNodeEventQueue
→ EventQueue.dispatchEvent()
→ AWT Component event listeners
The KeyboardHandler and MouseHandler convert hardware interrupts into AWT events:
- Keyboard scancodes →
KeyEvent(KEY_PRESSED, KEY_RELEASED, KEY_TYPED) - Mouse movement/clicks →
MouseEvent(MOUSE_MOVED, MOUSE_DRAGGED, MOUSE_CLICKED, etc.)
Swing peers implement standard AWT peer interfaces:
| Peer Interface | Implementation |
|---|---|
ButtonPeer |
SwingButtonPeer |
LabelPeer |
SwingLabelPeer |
FramePeer |
SwingFramePeer / DesktopFramePeer
|
WindowPeer |
SwingWindowPeer |
ContainerPeer |
SwingContainerPeer |
LightweightPeer |
SwingLightweightPeer |
| ... | ... |
Each implementation wraps the corresponding JComponent and delegates painting, event handling, and layout.
JNode includes a TrueType font subsystem:
-
TTFFontPeer— wraps a TrueType font file -
FontManager— service managing available fonts -
TextRenderer— renders glyphs to graphics context
Font rendering flows through the graphics pipeline to the framebuffer.
The SwingFramePeer (lines 54-167 in SwingFramePeer.java) implements FramePeer and ISwingContainerPeer. It wraps a SwingFrame (an internal JInternalFrame subclass) and manages:
- Window state (iconified, maximized, normal)
- Menu bar integration via
setMenuBar() - Addition to the desktop via
addToDesktop()
The DesktopFrame (lines 45-165 in DesktopFrame.java) extends JFrame and implements JNodeAwtContext. It serves as:
- The root window containing all other AWT windows
- A
JDesktopPanethat manages internal frames - Background color persistence via
Preferences - Size adjustment through
adjustDesktopSize()
The peer implementation uses SwingToolkit.invokeNowOrLater() to ensure thread-safe operations:
// From SwingFramePeer.java
public void setMenuBar(final MenuBar mb) {
SwingToolkit.invokeNowOrLater(new Runnable() {
public void run() { /* ... */ }
});
}This ensures AWT operations execute on the Event Dispatch Thread (EDT) while handling cases where peers may be created from different AppContext instances.
JNode's SwingBaseWindow is a JInternalFrame (SwingBaseWindow.java:52-53), so isSelected() is frequently true for windows that are not actually on top. This is a live source of bugs — an attempted fix for the resulting "clicking a background window does not raise it" / "clicking the taskbar button of a background-but-selected window iconifies it" cluster was written, documented here, and then reverted (see the timeline below).
| Commit | Date | Effect |
|---|---|---|
2a712bf8 |
2026-09-26 | Explicit-raise rewrite of activateWindow, DesktopManagerImpl.dragFrame/activateFrame overrides, WindowBar.isTopFrame, FrameWrapper toFront() + repaint chain |
50c7b00be |
2026-10-01 | Revert of the above (3 files, −57/+3). Only the rate-limited activation-failure logging survived |
What survives today is the logging half, which was orthogonal to the stacking behaviour and is not part of the revert.
gui/src/awt/org/jnode/awt/swingpeers/SwingToolkit.java:585-615:
if (f.isShowing() && !f.isSelected()) {
try {
f.setSelected(true);
activationWarningCount = 0;
} catch (PropertyVetoException e) { ... } catch (RuntimeException e) { ... }
}The !f.isSelected() guard is back, and there is no toFront(), no getDesktopPane().setSelectedFrame(f), and no repaint chain — so activating an already-"selected" background window is still a no-op. That is the pre-2a712bf8 behaviour, and it is deliberate: the revert restored it.
A JInternalFrame's setSelected(true) can throw a plain RuntimeException when the frame's UI/peer state is inconsistent. That is logged and swallowed, at most a few times:
-
private static final int MAX_ACTIVATION_WARNINGS = 3;andprivate int activationWarningCount;(:118-120) -
PropertyVetoException→log.debug("Internal frame refused selection", e)(:598-601) -
RuntimeException→ up to 2log.warn("Unable to activate internal frame", e), then onelog.warn("Further internal-frame activation failures suppressed", e), then silence (onlylog.debugafterwards) (:602-611) - A successful activation resets the counter to 0 (
:597)
The counter is incremented past the threshold (to MAX + 1) so the "further failures suppressed" message is emitted exactly once.
gui/src/desktop/org/jnode/desktop/classic/Desktop.java:267-293. The activateFrame override does not exist any more, and dragFrame is back to a bare bounds update:
@Override
public void dragFrame(JComponent frame, int newX, int newY) { // :270-273
setBoundsForFrame(frame, newX, newY, frame.getWidth(), frame.getHeight());
}DefaultDesktopManager.dragFrame is a no-op, so this override exists at all purely to make dragging move the window; there is no toFront() and no repaint. Fields: taskBar (:69), desktopPane (:71), desktopFrame (:74, a JNodeAwtContext). Neighbouring pre-existing overrides: deiconifyFrame :278-284, iconifyFrame :289-295.
gui/src/desktop/org/jnode/desktop/classic/WindowBar.java. isTopFrame does not exist any more, and FrameWrapper has no toFront() and no repaint chain:
if (frame.isIcon()) {
frame.setIcon(false);
frame.setSelected(true);
} else if (frame.isSelected()) { // was: frame.isSelected() && isTopFrame(frame)
frame.setSelected(false);
frame.setIcon(true);
selectNextFrame(frame.getDesktopPane());
} else {
frame.setSelected(true);
}Consequence, unchanged from before the fix attempt: because isSelected() can be true for several windows at once, clicking the taskbar button of a background-but-"selected" window iconifies it instead of raising it. This is the open half of the bug the revert exposed.
selectNextFrame(JDesktopPane) (:111-131) is unchanged and still picks the topmost remaining frame — the one with the lowest non-negative getComponentZOrder among visible non-iconified frames (:114-124) — but it only calls sel.setSelected(true), with no toFront() or repaint.
See DesktopFrame for the wider window-manager picture and JNode-Graphics2D for the paint-clipping half of the same bug family.
- No native windowing: AWT windows are simulated internally; they don't correspond to OS-level windows
-
Reference counting: GUI mode uses
incRefCount()/decRefCount()to track active users; when count reaches zero, graphics are released -
AppContext isolation: Peers must be created in the same
AppContextas the desktop frame to ensure event dispatch works correctly -
Look and feel:
SwingToolkitsetsMetalLookAndFeelwithOceanThemeas default -
Desktop ownership: The first
Framecreated becomes the desktop; subsequent frames become internal frames -
isSelected()is not "frontmost" — because a JNode window is aJInternalFrame, several windows report selected at once. There is no z-order-aware "am I on top" helper in the tree any more:WindowBar.isTopFramewas added on 2026-09-26 and reverted on 2026-10-01. UsegetComponentZOrdercomparisons directly (lowest index wins). -
The 2026-09-26 stacking fix was reverted —
toFront(),DesktopManagerImpl.activateFrameand theisTopFrameiconify gate are all gone. If you are chasing "taskbar click iconifies the wrong window" or "background window does not come forward", you are looking at pre-existing behaviour, not a regression. -
Swing z-order is inverted: lower
getComponentZOrderindex is on top.otherZ < frameZmeans "in front of me". - Occluded frames still repaint in full — there is no damage tracking, so window paint clipping (JNode-Graphics2D) is what prevents lower windows from painting over higher ones.
-
GUI tests must be headless-safe — constructing a real
java.awt.Frame(orSwingBaseWindow, whose constructor chain issuper(title, true, true, true, true)) requires a display that a forked test JVM does not have. Use a plainJInternalFrame.
- GUI-AWT — High-level GUI and AWT overview
- Font-Rendering — TrueType and BDF font rendering system.
- Video-Driver-Architecture — Framebuffer and Surface abstraction
- JNode-Graphics2D — Graphics2D implementations (JNodeSurfaceGraphics2D, JNodeGraphics2D) and window paint clipping
- DesktopFrame — Desktop window manager, taskbar, application launching
- Driver-Framework — How drivers are registered and loaded
- Plugin-System — How the AWT plugin is loaded at boot
- Architecture — Overall system structure