Skip to content

DesktopFrame

opencode-agent[bot] edited this page Oct 4, 2026 · 2 revisions

DesktopFrame

Desktop window manager providing window decoration, task bar, and application launching capabilities for JNode's GUI system.

Overview

JNode implements a complete desktop environment independent of any host operating system. The DesktopFrame system consists of two interrelated components:

  1. Swing DesktopFrame (gui/src/awt/org/jnode/awt/swingpeers/DesktopFrame.java) - The root AWT Frame that serves as the desktop canvas, hosting all other windows as internal frames
  2. Desktop Manager (gui/src/desktop/org/jnode/desktop/) - The task bar and application launcher that provides window management and application access

This dual-layer approach allows JNode to simulate a complete windowing system using Swing's JDesktopPane and JInternalFrame components, while providing a custom control bar for application management.

Key Components

Class Location Purpose
DesktopFrame gui/src/awt/org/jnode/awt/swingpeers/DesktopFrame.java Root JFrame containing the JDesktopPane; implements JNodeAwtContext
DesktopFramePeer gui/src/awt/org/jnode/awt/swingpeers/DesktopFramePeer.java AWT peer for DesktopFrame
Desktop gui/src/desktop/org/jnode/desktop/classic/Desktop.java Runnable that initializes the control bar and integrates with the AWT context
ControlBar gui/src/desktop/org/jnode/desktop/classic/ControlBar.java Bottom panel containing ApplicationBar and WindowBar
ApplicationBar gui/src/desktop/org/jnode/desktop/classic/ApplicationBar.java Application launcher with plugin extension point
WindowBar gui/src/desktop/org/jnode/desktop/classic/WindowBar.java Button bar showing open internal frames; also owns selectNextFrame
TaskBar gui/src/desktop/org/jnode/desktop/classic/TaskBar.java Container holding the WindowBar (and the clock)
DesktopPlugin gui/src/desktop/org/jnode/desktop/DesktopPlugin.java Plugin descriptor for desktop subsystem

There are two generations of these classes: the older org.jnode.desktop.* (Desktop.java, WindowBar.java, ControlBar.java, ApplicationBar.java) and the live org.jnode.desktop.classic.* (Desktop.java, WindowBar.java, TaskBar.java). The descriptor gui/descriptors/org.jnode.desktop.xml exports both (:38-39) but only instantiates org.jnode.desktop.DesktopPlugin, and every recent change lands in the classic package. Always check the classic/ path.

Architecture

Component Hierarchy

DesktopFrame (JFrame)
└── JDesktopPane
    └── JInternalFrame (multiple - one per AWT Window)
         └── SwingBaseWindow (peer)
              └── AWT Window/Component

ControlBar (JPanel, positioned at bottom)
├── ApplicationBar (center)
└── WindowBar (east)

Initialization Flow

1. Boot → AWTPlugin.startPlugin()
2. Application calls Toolkit.getDefaultToolkit()
3. JNodeToolkit creates SwingToolkit
4. SwingToolkit.onInitialize() creates DesktopFrame
5. Desktop.run() creates ControlBar
6. ControlBar integrates with DesktopFrame's JDesktopPane

Desktop Integration

The Desktop class (in gui/src/desktop/) bridges the control bar to the AWT context:

// From Desktop.java
public void run() {
    // Get the AWT context from JNodeToolkit
    final JNodeToolkit tk = JNodeToolkit.getJNodeToolkit();
    final JNodeAwtContext ctx = tk.getAwtContext();
    final JDesktopPane desktop = ctx.getDesktop();

    // Add control bar at bottom
    awtRoot.add(controlBar);
    controlBar.setBounds(0, h - controlBarHeight, w, controlBarHeight);

    // Add Halt and Reboot buttons
    controlBar.getApplicationBar().addApp("Halt", ...);
    controlBar.getApplicationBar().addApp("Reboot", ...);
}

Window Management

Internal Frame Handling

All AWT Window and Frame objects are rendered as JInternalFrame instances within the JDesktopPane:

  1. When a new AWT Window is created, SwingToolkit creates a corresponding SwingBaseWindow
  2. SwingBaseWindow wraps a JInternalFrame that handles native-looking decorations
  3. The WindowBar tracks all open internal frames as FrameWrapper buttons in a HashMap

WindowBar Functionality

The WindowBar provides window switching. It is a JPanel with a FlowLayout(LEFT, 1, 1) and a lowered BevelBorder, holding one FrameWrapper (JButton) per tracked frame in a HashMap<JInternalFrame, FrameWrapper> — not a JList with a selection listener:

// From WindowBar.java
public void addFrame(final JInternalFrame frame) {
    ...
    final FrameWrapper wrapper = new FrameWrapper(frame);
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            wrappers.put(frame, wrapper);
            add(wrapper);
            revalidate();
            repaint();
        }
    });
}

// FrameWrapper's toggle: raise, or iconify if it is already "selected"
public void actionPerformed(ActionEvent event) {
    if (frame.isIcon()) {
        frame.setIcon(false);
        frame.setSelected(true);
    } else if (frame.isSelected()) {          // was: ... && isTopFrame(frame), reverted 2026-10-01
        frame.setSelected(false);
        frame.setIcon(true);
        selectNextFrame(frame.getDesktopPane());
    } else {
        frame.setSelected(true);
    }
}

addFrame skips frames whose ISwingPeer AWT component is a Frame that is not undecorated, and for undecorated/other components installs a componentHidden listener that calls selectNextFrame instead of adding a button. selectNextFrame picks the visible, non-iconified frame with the lowest non-negative getComponentZOrder — Swing's inverted z-order means lowest index is on top.

FrameWrapper also registers an InternalFrameListener that paints the button white on internalFrameActivated, light gray on internalFrameDeactivated, and removes the wrapper on internalFrameClosing.

Since isSelected() can be true for several windows at once and there is no z-order gate (see AWT-Peer-Implementation for the reverted isTopFrame), clicking the button of a background-but-selected window iconifies it rather than raising it.

Application Launching

The ApplicationBar uses JNode's plugin extension system to discover and launch applications:

// From ApplicationBar.java
private void reloadApps() {
    final Extension[] exts = ep.getExtensions();
    for (int i = 0; i < exts.length; i++) {
        final ConfigurationElement[] elems = ext[i].getConfigurationElements();
        for (ConfigurationElement ce : elems) {
            final String name = ce.getAttribute("name");
            final String className = ce.getAttribute("class");
            addApp(name, new LaunchListener(className));
        }
    }
}

final void startApp(final String name, final String className) {
    // Reflection to invoke main(String[]) method
    final Class<?> cls = cl.loadClass(className);
    final Method main = cls.getMethod("main", mainTypes);
    main.invoke(null, new Object[]{new String[0]});
}

Applications are declared in plugin descriptors:

<extension point="org.jnode.desktop.apps">
    <app name="FileManager" class="org.jnode.shell.file.FileManager"/>
</extension>

Gotchas

  • Plugin ClassLoader Requirement: The Desktop class must be loaded via a PluginClassLoader to access the applications extension point. This is enforced at runtime with an AWTError if not.
  • Swing Dependency: The entire desktop system depends on Swing (javax.swing), so the swingpeers plugin must be loaded for GUI operation.
  • Background Image: DesktopFrame supports a background image that is painted in the bottom-right corner with a 30x20 pixel offset.
  • Color Preferences: Desktop background color is persisted via java.util.prefs.Preferences and restored on each launch.
  • DesktopPane Size Management: When screen resolution changes, DesktopFrame.adjustDesktopSize() must be called to resize the desktop and invalidate the component tree.

Related Pages

Clone this wiki locally