diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java
index abd8b2446dc..f769e24efe0 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java
@@ -1774,6 +1774,23 @@ public static boolean isSystemDarkTheme () {
return OS.isSystemDarkAppearance();
}
+/**
+ * Returns true
if SWT is running in headless mode, else
+ * returns false
.
+ *
+ * In headless mode, SWT widgets do not create native platform resources
+ * and operations are no-ops or return default values.
+ *
+ *
+ * @return true
if SWT is running in headless mode, else
+ * returns false
.
+ *
+ * @since 3.132
+ */
+public static boolean isHeadless () {
+ return false;
+}
+
int getLastEventTime () {
NSEvent event = application != null ? application.currentEvent() : null;
if (event == null) return 0;
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java
index 5b52c106f48..5ae4b85a738 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java
@@ -2613,6 +2613,23 @@ public static boolean isSystemDarkTheme () {
return themeDark;
}
+/**
+ * Returns true
if SWT is running in headless mode, else
+ * returns false
.
+ *
+ * In headless mode, SWT widgets do not create native platform resources
+ * and operations are no-ops or return default values.
+ *
+ *
+ * @return true
if SWT is running in headless mode, else
+ * returns false
.
+ *
+ * @since 3.132
+ */
+public static boolean isHeadless () {
+ return false;
+}
+
int getLastEventTime () {
return lastEventTime;
}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/graphics/Device.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/graphics/Device.java
new file mode 100644
index 00000000000..4e8ff494490
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/graphics/Device.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+import org.eclipse.swt.*;
+
+/**
+ * Headless implementation of Device for SWT.
+ */
+public abstract class Device implements Drawable {
+
+ protected static final int CHANGE_SCALEFACTOR = 1;
+
+ public static boolean DEBUG;
+ boolean debug = DEBUG;
+ boolean tracking = DEBUG;
+ Error[] errors;
+ Object[] objects;
+ Object trackingLock;
+
+ volatile boolean disposed;
+
+ int warningLevel;
+
+ static Device[] Devices = new Device[4];
+
+ static final Color COLOR_BLACK, COLOR_DARK_RED, COLOR_DARK_GREEN, COLOR_DARK_YELLOW, COLOR_DARK_BLUE;
+ static final Color COLOR_DARK_MAGENTA, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY, COLOR_RED, COLOR_TRANSPARENT;
+ static final Color COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE;
+
+ static {
+ COLOR_TRANSPARENT = new Color(0xFF, 0xFF, 0xFF, 0);
+ COLOR_BLACK = new Color(0, 0, 0);
+ COLOR_DARK_RED = new Color(0x80, 0, 0);
+ COLOR_DARK_GREEN = new Color(0, 0x80, 0);
+ COLOR_DARK_YELLOW = new Color(0x80, 0x80, 0);
+ COLOR_DARK_BLUE = new Color(0, 0, 0x80);
+ COLOR_DARK_MAGENTA = new Color(0x80, 0, 0x80);
+ COLOR_DARK_CYAN = new Color(0, 0x80, 0x80);
+ COLOR_GRAY = new Color(0xC0, 0xC0, 0xC0);
+ COLOR_DARK_GRAY = new Color(0x80, 0x80, 0x80);
+ COLOR_RED = new Color(0xFF, 0, 0);
+ COLOR_GREEN = new Color(0, 0xFF, 0);
+ COLOR_YELLOW = new Color(0xFF, 0xFF, 0);
+ COLOR_BLUE = new Color(0, 0, 0xFF);
+ COLOR_MAGENTA = new Color(0xFF, 0, 0xFF);
+ COLOR_CYAN = new Color(0, 0xFF, 0xFF);
+ COLOR_WHITE = new Color(0xFF, 0xFF, 0xFF);
+ }
+
+ Font systemFont;
+ Point dpi;
+
+ protected static Device CurrentDevice;
+ protected static Runnable DeviceFinder;
+ static {
+ try {
+ Class.forName("org.eclipse.swt.widgets.Display");
+ } catch (ClassNotFoundException e) {}
+ }
+
+ static synchronized Device getDevice() {
+ if (DeviceFinder != null) DeviceFinder.run();
+ Device device = CurrentDevice;
+ CurrentDevice = null;
+ return device;
+ }
+
+ public Device() {
+ this(null);
+ }
+
+ public Device(DeviceData data) {
+ synchronized (Device.class) {
+ if (data != null) {
+ debug = data.debug;
+ tracking = data.tracking;
+ }
+ if (tracking) {
+ startTracking();
+ }
+ create(data);
+ init();
+ register(this);
+ }
+ }
+
+ protected void checkDevice() {
+ if (disposed) SWT.error(SWT.ERROR_DEVICE_DISPOSED);
+ }
+
+ protected void create(DeviceData data) {
+ }
+
+ protected void destroy() {
+ }
+
+ public void dispose() {
+ synchronized (Device.class) {
+ if (isDisposed()) return;
+ checkDevice();
+ release();
+ destroy();
+ deregister(this);
+ disposed = true;
+ if (tracking) {
+ stopTracking();
+ }
+ }
+ }
+
+ void dispose_Object(Object object) {
+ for (int i = 0; i < objects.length; i++) {
+ if (objects[i] == object) {
+ objects[i] = null;
+ errors[i] = null;
+ return;
+ }
+ }
+ }
+
+ static void deregister(Device device) {
+ synchronized (Device.class) {
+ for (int i = 0; i < Devices.length; i++) {
+ if (device == Devices[i]) Devices[i] = null;
+ }
+ }
+ }
+
+ public Rectangle getBounds() {
+ checkDevice();
+ return new Rectangle(0, 0, 1024, 768);
+ }
+
+ public Rectangle getClientArea() {
+ return getBounds();
+ }
+
+ public int getDepth() {
+ return 24;
+ }
+
+ public Point getDPI() {
+ checkDevice();
+ if (dpi == null) {
+ dpi = new Point(96, 96);
+ }
+ return new Point(dpi.x, dpi.y);
+ }
+
+ public FontData[] getFontList(String faceName, boolean scalable) {
+ checkDevice();
+ return new FontData[0];
+ }
+
+ public Color getSystemColor(int id) {
+ checkDevice();
+ switch (id) {
+ case SWT.COLOR_BLACK: return COLOR_BLACK;
+ case SWT.COLOR_DARK_RED: return COLOR_DARK_RED;
+ case SWT.COLOR_DARK_GREEN: return COLOR_DARK_GREEN;
+ case SWT.COLOR_DARK_YELLOW: return COLOR_DARK_YELLOW;
+ case SWT.COLOR_DARK_BLUE: return COLOR_DARK_BLUE;
+ case SWT.COLOR_DARK_MAGENTA: return COLOR_DARK_MAGENTA;
+ case SWT.COLOR_DARK_CYAN: return COLOR_DARK_CYAN;
+ case SWT.COLOR_GRAY: return COLOR_GRAY;
+ case SWT.COLOR_DARK_GRAY: return COLOR_DARK_GRAY;
+ case SWT.COLOR_RED: return COLOR_RED;
+ case SWT.COLOR_GREEN: return COLOR_GREEN;
+ case SWT.COLOR_YELLOW: return COLOR_YELLOW;
+ case SWT.COLOR_BLUE: return COLOR_BLUE;
+ case SWT.COLOR_MAGENTA: return COLOR_MAGENTA;
+ case SWT.COLOR_CYAN: return COLOR_CYAN;
+ case SWT.COLOR_WHITE: return COLOR_WHITE;
+ case SWT.COLOR_TRANSPARENT: return COLOR_TRANSPARENT;
+ }
+ return COLOR_BLACK;
+ }
+
+ public Font getSystemFont() {
+ checkDevice();
+ if (systemFont == null) {
+ systemFont = new Font(this, new FontData("Sans", 10, SWT.NORMAL));
+ }
+ return systemFont;
+ }
+
+ public int getWarnings() {
+ checkDevice();
+ return warningLevel;
+ }
+
+ protected void init() {
+ }
+
+ @Override
+ public abstract boolean isAutoScalable();
+
+ public boolean isDisposed() {
+ synchronized (Device.class) {
+ return disposed;
+ }
+ }
+
+ Object[] new_Object(int size) {
+ return new Object[size];
+ }
+
+ protected void release() {
+ if (tracking) {
+ synchronized (trackingLock) {
+ for (int i = 0; i < objects.length; i++) {
+ if (objects[i] != null) {
+ System.err.println("WARNING: Resource leaked: " + objects[i]);
+ }
+ }
+ objects = null;
+ errors = null;
+ }
+ }
+ }
+
+ static synchronized void register(Device device) {
+ for (int i = 0; i < Devices.length; i++) {
+ if (Devices[i] == null) {
+ Devices[i] = device;
+ return;
+ }
+ }
+ Device[] newDevices = new Device[Devices.length + 4];
+ System.arraycopy(Devices, 0, newDevices, 0, Devices.length);
+ newDevices[Devices.length] = device;
+ Devices = newDevices;
+ }
+
+ public void setWarnings(boolean warnings) {
+ checkDevice();
+ warningLevel = warnings ? 1 : 0;
+ }
+
+ void startTracking() {
+ synchronized (Device.class) {
+ if (trackingLock == null) {
+ trackingLock = new Object();
+ objects = new Object[128];
+ errors = new Error[128];
+ }
+ }
+ }
+
+ void stopTracking() {
+ synchronized (Device.class) {
+ objects = null;
+ errors = null;
+ trackingLock = null;
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/graphics/DeviceData.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/graphics/DeviceData.java
new file mode 100644
index 00000000000..e3e76e6b271
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/graphics/DeviceData.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+/**
+ * Headless implementation of DeviceData for SWT.
+ */
+public class DeviceData {
+
+ public String display_name;
+ public String application_name;
+ public String application_class;
+
+ public boolean debug;
+ public boolean tracking;
+ public Error[] errors;
+ public Object[] objects;
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Button.java
new file mode 100644
index 00000000000..c835c8e73cf
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Button.java
@@ -0,0 +1,141 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Button for SWT.
+ */
+public class Button extends Control {
+ String text = "";
+ Image image;
+ boolean selected;
+ boolean grayed;
+ int alignment = SWT.CENTER;
+
+public Button(Composite parent, int style) {
+ super(parent, checkStyle(style));
+}
+
+static int checkStyle(int style) {
+ style = checkBits(style, SWT.PUSH, SWT.ARROW, SWT.CHECK, SWT.RADIO, SWT.TOGGLE, 0);
+ if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
+ return checkBits(style, SWT.CENTER, SWT.LEFT, SWT.RIGHT, 0, 0, 0);
+ }
+ if ((style & (SWT.CHECK | SWT.RADIO)) != 0) {
+ return checkBits(style, SWT.LEFT, SWT.RIGHT, SWT.CENTER, 0, 0, 0);
+ }
+ if ((style & SWT.ARROW) != 0) {
+ style |= SWT.NO_FOCUS;
+ return checkBits(style, SWT.UP, SWT.DOWN, SWT.LEFT, SWT.RIGHT, 0, 0);
+ }
+ return style;
+}
+
+public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Selection, typedListener);
+ addListener(SWT.DefaultSelection, typedListener);
+}
+
+public int getAlignment() {
+ checkWidget();
+ if ((style & SWT.ARROW) != 0) {
+ if ((style & SWT.UP) != 0) return SWT.UP;
+ if ((style & SWT.DOWN) != 0) return SWT.DOWN;
+ if ((style & SWT.LEFT) != 0) return SWT.LEFT;
+ if ((style & SWT.RIGHT) != 0) return SWT.RIGHT;
+ return SWT.UP;
+ }
+ return alignment;
+}
+
+public boolean getGrayed() {
+ checkWidget();
+ if ((style & SWT.CHECK) == 0) return false;
+ return grayed;
+}
+
+public Image getImage() {
+ checkWidget();
+ return image;
+}
+
+public boolean getSelection() {
+ checkWidget();
+ if ((style & (SWT.CHECK | SWT.RADIO | SWT.TOGGLE)) == 0) return false;
+ return selected;
+}
+
+public String getText() {
+ checkWidget();
+ return text;
+}
+
+public void removeSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) return;
+ eventTable.unhook(SWT.Selection, listener);
+ eventTable.unhook(SWT.DefaultSelection, listener);
+}
+
+public void setAlignment(int alignment) {
+ checkWidget();
+ if ((style & SWT.ARROW) != 0) {
+ if ((style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) == 0) return;
+ style &= ~(SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT);
+ style |= alignment & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT);
+ return;
+ }
+ if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return;
+ this.alignment = alignment;
+}
+
+public void setGrayed(boolean grayed) {
+ checkWidget();
+ if ((style & SWT.CHECK) == 0) return;
+ this.grayed = grayed;
+}
+
+public void setImage(Image image) {
+ checkWidget();
+ if ((style & SWT.ARROW) != 0) return;
+ this.image = image;
+}
+
+public void setSelection(boolean selected) {
+ checkWidget();
+ if ((style & (SWT.CHECK | SWT.RADIO | SWT.TOGGLE)) == 0) return;
+ this.selected = selected;
+}
+
+public void setText(String string) {
+ checkWidget();
+ if (string == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if ((style & SWT.ARROW) != 0) return;
+ text = string;
+}
+
+@Override
+String getNameText() {
+ return getText();
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Canvas.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Canvas.java
new file mode 100644
index 00000000000..853c01f8c2e
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Canvas.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Canvas for SWT.
+ */
+public class Canvas extends Composite {
+ Caret caret;
+
+public Canvas() {
+ // No-op
+}
+
+public Canvas(Display display, int style) {
+ super(null, style);
+ this.display = display;
+}
+
+public Canvas(Composite parent, int style) {
+ super(parent, style);
+}
+
+public void drawBackground(GC gc, int x, int y, int width, int height) {
+ checkWidget();
+ // No-op in headless mode
+}
+
+public Caret getCaret() {
+ checkWidget();
+ return caret;
+}
+
+public void scroll(int destX, int destY, int x, int y, int width, int height, boolean all) {
+ checkWidget();
+ // No-op in headless mode
+}
+
+public void setCaret(Caret caret) {
+ checkWidget();
+ this.caret = caret;
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Caret.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Caret.java
new file mode 100644
index 00000000000..226f6470d75
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Caret.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Caret for SWT.
+ */
+public class Caret extends Widget {
+ Canvas parent;
+ Image image;
+ Font font;
+ int x, y, width, height;
+ boolean visible;
+
+public Caret(Canvas parent, int style) {
+ super(parent, style);
+ this.parent = parent;
+ width = 1;
+ height = 10;
+}
+
+public Rectangle getBounds() {
+ checkWidget();
+ return new Rectangle(x, y, width, height);
+}
+
+public Font getFont() {
+ checkWidget();
+ return font;
+}
+
+public Image getImage() {
+ checkWidget();
+ return image;
+}
+
+public Point getLocation() {
+ checkWidget();
+ return new Point(x, y);
+}
+
+public Canvas getParent() {
+ checkWidget();
+ return parent;
+}
+
+public Point getSize() {
+ checkWidget();
+ return new Point(width, height);
+}
+
+public boolean getVisible() {
+ checkWidget();
+ return visible;
+}
+
+public boolean isVisible() {
+ checkWidget();
+ return visible && parent.isVisible();
+}
+
+public void setBounds(int x, int y, int width, int height) {
+ checkWidget();
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+}
+
+public void setBounds(Rectangle rect) {
+ checkWidget();
+ if (rect == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setBounds(rect.x, rect.y, rect.width, rect.height);
+}
+
+public void setFont(Font font) {
+ checkWidget();
+ this.font = font;
+}
+
+public void setImage(Image image) {
+ checkWidget();
+ this.image = image;
+}
+
+public void setLocation(int x, int y) {
+ checkWidget();
+ this.x = x;
+ this.y = y;
+}
+
+public void setLocation(Point location) {
+ checkWidget();
+ if (location == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setLocation(location.x, location.y);
+}
+
+public void setSize(int width, int height) {
+ checkWidget();
+ this.width = width;
+ this.height = height;
+}
+
+public void setSize(Point size) {
+ checkWidget();
+ if (size == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setSize(size.x, size.y);
+}
+
+public void setVisible(boolean visible) {
+ checkWidget();
+ this.visible = visible;
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Composite.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Composite.java
new file mode 100644
index 00000000000..dd93791edf6
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Composite.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Composite for SWT.
+ */
+public class Composite extends Scrollable {
+ Layout layout;
+ Control[] tabList;
+ int layoutCount, backgroundMode;
+ List children = new ArrayList<>();
+
+public Composite() {
+ // No-op
+}
+
+public Composite(Composite parent, int style) {
+ super(parent, checkStyle(style));
+}
+
+static int checkStyle(int style) {
+ style &= ~SWT.TRANSPARENT;
+ return style;
+}
+
+void addChild(Control control) {
+ children.add(control);
+}
+
+void removeChild(Control control) {
+ children.remove(control);
+}
+
+public Control[] getChildren() {
+ checkWidget();
+ return children.toArray(new Control[children.size()]);
+}
+
+public int getBackgroundMode() {
+ checkWidget();
+ return backgroundMode;
+}
+
+public Layout getLayout() {
+ checkWidget();
+ return layout;
+}
+
+public boolean getLayoutDeferred() {
+ checkWidget();
+ return layoutCount > 0;
+}
+
+public Control[] getTabList() {
+ checkWidget();
+ if (tabList != null) return tabList;
+ int count = 0;
+ Control[] list = getChildren();
+ for (Control child : list) {
+ if (child.isTabGroup()) count++;
+ }
+ Control[] result = new Control[count];
+ int index = 0;
+ for (Control child : list) {
+ if (child.isTabGroup()) {
+ result[index++] = child;
+ }
+ }
+ return result;
+}
+
+public boolean isLayoutDeferred() {
+ checkWidget();
+ return findDeferredControl() != null;
+}
+
+Composite findDeferredControl() {
+ return layoutCount > 0 ? this : (parent != null ? parent.findDeferredControl() : null);
+}
+
+public void layout() {
+ checkWidget();
+ layout(true);
+}
+
+public void layout(boolean changed) {
+ checkWidget();
+ layout(changed, false);
+}
+
+public void layout(boolean changed, boolean all) {
+ checkWidget();
+ if (layout == null) return;
+ if (layoutCount == 0) {
+ layout.layout(this, changed);
+ if (all) {
+ for (Control child : children) {
+ if (child instanceof Composite) {
+ ((Composite) child).layout(changed, all);
+ }
+ }
+ }
+ }
+}
+
+public void layout(Control[] changed) {
+ checkWidget();
+ if (changed == null) error(SWT.ERROR_INVALID_ARGUMENT);
+ layout(changed, SWT.NONE);
+}
+
+public void layout(Control[] changed, int flags) {
+ checkWidget();
+ if (changed != null) {
+ for (Control child : changed) {
+ if (child == null) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (child.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (child.parent != this) error(SWT.ERROR_INVALID_PARENT);
+ }
+ }
+ if (layout == null) return;
+ if ((flags & SWT.DEFER) != 0) {
+ setLayoutDeferred(true);
+ return;
+ }
+ layout.layout(this, (flags & SWT.CHANGED) != 0);
+}
+
+public void setBackgroundMode(int mode) {
+ checkWidget();
+ backgroundMode = mode;
+}
+
+public void setLayout(Layout layout) {
+ checkWidget();
+ this.layout = layout;
+}
+
+public void setLayoutDeferred(boolean defer) {
+ if (!defer) {
+ if (--layoutCount == 0) {
+ if ((state & LAYOUT_CHILD) != 0 || (state & LAYOUT_NEEDED) != 0) {
+ updateLayout(true);
+ }
+ }
+ } else {
+ layoutCount++;
+ }
+}
+
+void updateLayout(boolean all) {
+ Composite parent = findDeferredControl();
+ if (parent != null) {
+ parent.state |= LAYOUT_CHILD;
+ return;
+ }
+ if ((state & LAYOUT_NEEDED) != 0) {
+ boolean changed = (state & LAYOUT_CHANGED) != 0;
+ state &= ~(LAYOUT_NEEDED | LAYOUT_CHANGED);
+ layout(changed, all);
+ }
+ if (all) {
+ state &= ~LAYOUT_CHILD;
+ for (Control child : children) {
+ if (child instanceof Composite) {
+ ((Composite) child).updateLayout(all);
+ }
+ }
+ }
+}
+
+public void setTabList(Control[] tabList) {
+ checkWidget();
+ if (tabList != null) {
+ for (Control control : tabList) {
+ if (control == null) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (control.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (control.parent != this) error(SWT.ERROR_INVALID_PARENT);
+ }
+ Control[] newList = new Control[tabList.length];
+ System.arraycopy(tabList, 0, newList, 0, tabList.length);
+ tabList = newList;
+ }
+ this.tabList = tabList;
+}
+
+@Override
+protected void checkSubclass() {
+ // Allow subclassing
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Control.java
new file mode 100644
index 00000000000..624864e71f3
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Control.java
@@ -0,0 +1,349 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Control for SWT.
+ */
+public abstract class Control extends Widget implements Drawable {
+ Composite parent;
+ String toolTipText;
+ Object layoutData;
+ Menu menu;
+ Cursor cursor;
+ Font font;
+ Color foreground, background;
+ Image backgroundImage;
+ boolean enabled = true;
+ boolean visible = true;
+ int x, y, width, height;
+
+public Control() {
+ // No-op
+}
+
+public Control(Composite parent, int style) {
+ super(parent, style);
+ this.parent = parent;
+ if (parent != null) {
+ parent.addChild(this);
+ }
+ width = DEFAULT_WIDTH;
+ height = DEFAULT_HEIGHT;
+}
+
+public Point computeSize(int wHint, int hHint) {
+ return computeSize(wHint, hHint, true);
+}
+
+public Point computeSize(int wHint, int hHint, boolean changed) {
+ checkWidget();
+ int width = DEFAULT_WIDTH;
+ int height = DEFAULT_HEIGHT;
+ if (wHint != SWT.DEFAULT) width = wHint;
+ if (hHint != SWT.DEFAULT) height = hHint;
+ return new Point(width, height);
+}
+
+public boolean forceFocus() {
+ checkWidget();
+ return false;
+}
+
+public Color getBackground() {
+ checkWidget();
+ if (background != null) return background;
+ return display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
+}
+
+public Image getBackgroundImage() {
+ checkWidget();
+ return backgroundImage;
+}
+
+public Rectangle getBounds() {
+ checkWidget();
+ return new Rectangle(x, y, width, height);
+}
+
+public Cursor getCursor() {
+ checkWidget();
+ return cursor;
+}
+
+public boolean getEnabled() {
+ checkWidget();
+ return enabled;
+}
+
+public Font getFont() {
+ checkWidget();
+ if (font != null) return font;
+ return parent != null ? parent.getFont() : display.getSystemFont();
+}
+
+public Color getForeground() {
+ checkWidget();
+ if (foreground != null) return foreground;
+ return display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
+}
+
+public Object getLayoutData() {
+ checkWidget();
+ return layoutData;
+}
+
+public Point getLocation() {
+ checkWidget();
+ return new Point(x, y);
+}
+
+public Menu getMenu() {
+ checkWidget();
+ return menu;
+}
+
+public Composite getParent() {
+ checkWidget();
+ return parent;
+}
+
+public Shell getShell() {
+ checkWidget();
+ return parent != null ? parent.getShell() : null;
+}
+
+public Point getSize() {
+ checkWidget();
+ return new Point(width, height);
+}
+
+public String getToolTipText() {
+ checkWidget();
+ return toolTipText;
+}
+
+public boolean getVisible() {
+ checkWidget();
+ return visible;
+}
+
+@Override
+public boolean isEnabled() {
+ checkWidget();
+ return getEnabled() && (parent != null ? parent.isEnabled() : true);
+}
+
+public boolean isFocusControl() {
+ checkWidget();
+ return display.focusControl == this;
+}
+
+public boolean isVisible() {
+ checkWidget();
+ return getVisible() && (parent != null ? parent.isVisible() : true);
+}
+
+public boolean isReparentable() {
+ checkWidget();
+ return true;
+}
+
+boolean isTabGroup() {
+ Control[] tabList = parent.tabList;
+ if (tabList != null) {
+ for (Control control : tabList) {
+ if (control == this) return true;
+ }
+ }
+ int bits = SWT.NONE;
+ return (bits & style) != 0;
+}
+
+public void moveAbove(Control control) {
+ checkWidget();
+ // No-op in headless mode
+}
+
+public void moveBelow(Control control) {
+ checkWidget();
+ // No-op in headless mode
+}
+
+public void pack() {
+ pack(true);
+}
+
+public void pack(boolean changed) {
+ checkWidget();
+ setSize(computeSize(SWT.DEFAULT, SWT.DEFAULT, changed));
+}
+
+public void redraw() {
+ checkWidget();
+ // No-op in headless mode
+}
+
+public void redraw(int x, int y, int width, int height, boolean all) {
+ checkWidget();
+ // No-op in headless mode
+}
+
+@Override
+void releaseParent() {
+ if (parent != null) parent.removeChild(this);
+}
+
+public boolean setFocus() {
+ checkWidget();
+ if (!isEnabled() || !isVisible()) return false;
+ display.focusControl = this;
+ return true;
+}
+
+public void setBackground(Color color) {
+ checkWidget();
+ background = color;
+}
+
+public void setBackgroundImage(Image image) {
+ checkWidget();
+ backgroundImage = image;
+}
+
+public void setBounds(int x, int y, int width, int height) {
+ checkWidget();
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+}
+
+public void setBounds(Rectangle rect) {
+ checkWidget();
+ if (rect == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setBounds(rect.x, rect.y, rect.width, rect.height);
+}
+
+public void setCursor(Cursor cursor) {
+ checkWidget();
+ this.cursor = cursor;
+}
+
+public void setEnabled(boolean enabled) {
+ checkWidget();
+ this.enabled = enabled;
+}
+
+public void setFont(Font font) {
+ checkWidget();
+ this.font = font;
+}
+
+public void setForeground(Color color) {
+ checkWidget();
+ foreground = color;
+}
+
+public void setLayoutData(Object layoutData) {
+ checkWidget();
+ this.layoutData = layoutData;
+}
+
+public void setLocation(int x, int y) {
+ checkWidget();
+ this.x = x;
+ this.y = y;
+}
+
+public void setLocation(Point location) {
+ checkWidget();
+ if (location == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setLocation(location.x, location.y);
+}
+
+public void setMenu(Menu menu) {
+ checkWidget();
+ this.menu = menu;
+}
+
+public boolean setParent(Composite parent) {
+ checkWidget();
+ if (parent == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (parent.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (this.parent == parent) return true;
+ if (this.parent != null) this.parent.removeChild(this);
+ this.parent = parent;
+ parent.addChild(this);
+ return true;
+}
+
+public void setSize(int width, int height) {
+ checkWidget();
+ this.width = width;
+ this.height = height;
+}
+
+public void setSize(Point size) {
+ checkWidget();
+ if (size == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setSize(size.x, size.y);
+}
+
+public void setToolTipText(String string) {
+ checkWidget();
+ toolTipText = string;
+}
+
+public void setVisible(boolean visible) {
+ checkWidget();
+ this.visible = visible;
+}
+
+public Point toControl(int x, int y) {
+ checkWidget();
+ return new Point(x - this.x, y - this.y);
+}
+
+public Point toControl(Point point) {
+ checkWidget();
+ if (point == null) error(SWT.ERROR_NULL_ARGUMENT);
+ return toControl(point.x, point.y);
+}
+
+public Point toDisplay(int x, int y) {
+ checkWidget();
+ return new Point(x + this.x, y + this.y);
+}
+
+public Point toDisplay(Point point) {
+ checkWidget();
+ if (point == null) error(SWT.ERROR_NULL_ARGUMENT);
+ return toDisplay(point.x, point.y);
+}
+
+public void update() {
+ checkWidget();
+ // No-op in headless mode
+}
+
+@Override
+public boolean isAutoScalable() {
+ return true;
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Decorations.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Decorations.java
new file mode 100644
index 00000000000..695358e194b
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Decorations.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Decorations for SWT.
+ */
+public class Decorations extends Canvas {
+ String text = "";
+ Image image;
+ Image[] images = new Image[0];
+ Menu menuBar;
+ Button defaultButton, saveDefault;
+
+public Decorations() {
+ // No-op
+}
+
+public Decorations(Display display, int style) {
+ super(display, style);
+}
+
+public Decorations(Composite parent, int style) {
+ super(parent, style);
+}
+
+public Button getDefaultButton() {
+ checkWidget();
+ return defaultButton;
+}
+
+public Image getImage() {
+ checkWidget();
+ return image;
+}
+
+public Image[] getImages() {
+ checkWidget();
+ if (images == null) return new Image[0];
+ Image[] result = new Image[images.length];
+ System.arraycopy(images, 0, result, 0, images.length);
+ return result;
+}
+
+public Menu getMenuBar() {
+ checkWidget();
+ return menuBar;
+}
+
+public String getText() {
+ checkWidget();
+ return text;
+}
+
+public void setDefaultButton(Button button) {
+ checkWidget();
+ if (button != null) {
+ if (button.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (button.getShell() != this) error(SWT.ERROR_INVALID_PARENT);
+ if ((button.style & SWT.PUSH) == 0) error(SWT.ERROR_INVALID_ARGUMENT);
+ }
+ defaultButton = button;
+}
+
+public void setImage(Image image) {
+ checkWidget();
+ this.image = image;
+}
+
+public void setImages(Image[] images) {
+ checkWidget();
+ if (images != null) {
+ this.images = new Image[images.length];
+ System.arraycopy(images, 0, this.images, 0, images.length);
+ } else {
+ this.images = new Image[0];
+ }
+}
+
+public void setMenuBar(Menu menu) {
+ checkWidget();
+ if (menuBar == menu) return;
+ if (menu != null) {
+ if ((menu.style & SWT.BAR) == 0) error(SWT.ERROR_MENU_NOT_BAR);
+ if (menu.parent != this) error(SWT.ERROR_INVALID_PARENT);
+ }
+ menuBar = menu;
+}
+
+public void setText(String string) {
+ checkWidget();
+ if (string == null) error(SWT.ERROR_NULL_ARGUMENT);
+ text = string;
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Display.java
new file mode 100644
index 00000000000..0d0ed884998
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Display.java
@@ -0,0 +1,605 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Display for SWT.
+ * This implementation provides no-op or default implementations
+ * for running SWT code in a headless environment.
+ */
+public class Display extends Device implements Executor {
+
+ static Display Default;
+ static String APP_NAME = "SWT";
+ static String APP_VERSION = "";
+
+ /* Widget Table */
+ Widget[] widgetTable;
+ int freeSlot;
+ static final int GROW_SIZE = 1024;
+
+ /* Synchronization */
+ Synchronizer synchronizer = new Synchronizer(this);
+ Consumer runtimeExceptionHandler = DefaultExceptionHandler.RUNTIME_EXCEPTION_HANDLER;
+ Consumer errorHandler = DefaultExceptionHandler.RUNTIME_ERROR_HANDLER;
+ Thread thread;
+
+ /* Shells */
+ Shell[] shells = new Shell[0];
+ Shell activeShell;
+
+ /* Modality */
+ Shell[] modalShells;
+
+ /* Disposal */
+ Runnable[] disposeList;
+
+ /* Event Handling */
+ EventTable eventTable, filterTable;
+ Event[] eventQueue;
+
+ /* Timers */
+ int[] timerIds;
+ Runnable[] timerList;
+ int nextTimerId = 1;
+
+ /* Deferred Layout */
+ Composite[] layoutDeferred;
+ int layoutDeferredCount;
+
+ /* System Resources */
+ Font systemFont;
+
+ /* Focus */
+ Control focusControl;
+
+ /* Disposed flag */
+ boolean disposed;
+
+ /* Package prefix */
+ static final String PACKAGE_PREFIX = "org.eclipse.swt.widgets.";
+
+public Display() {
+ this(null);
+}
+
+public Display(DeviceData data) {
+ super(data);
+ thread = Thread.currentThread();
+ synchronized (Device.class) {
+ if (Default == null) Default = this;
+ }
+ widgetTable = new Widget[GROW_SIZE];
+}
+
+@Override
+protected void create(DeviceData data) {
+ checkSubclass();
+ synchronizer = new Synchronizer(this);
+ systemFont = new Font(this, new FontData("Sans", 10, SWT.NORMAL));
+}
+
+@Override
+protected void init() {
+ super.init();
+}
+
+public void addFilter(int eventType, Listener listener) {
+ checkDevice();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (filterTable == null) filterTable = new EventTable();
+ filterTable.hook(eventType, listener);
+}
+
+public void addListener(int eventType, Listener listener) {
+ checkDevice();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) eventTable = new EventTable();
+ eventTable.hook(eventType, listener);
+}
+
+public void asyncExec(Runnable runnable) {
+ synchronized (Device.class) {
+ if (isDisposed()) error(SWT.ERROR_DEVICE_DISPOSED);
+ synchronizer.asyncExec(runnable);
+ }
+}
+
+@Override
+public void execute(Runnable runnable) {
+ Objects.requireNonNull(runnable);
+ if (thread == Thread.currentThread()) {
+ runnable.run();
+ } else {
+ syncExec(runnable);
+ }
+}
+
+public void beep() {
+ checkDevice();
+ // No-op in headless mode
+}
+
+@Override
+public void close() {
+ checkDevice();
+ Event event = new Event();
+ sendEvent(SWT.Close, event);
+ if (event.doit) dispose();
+}
+
+@Override
+protected void destroy() {
+ if (disposed) return;
+ disposed = true;
+ Shell[] shells = getShells();
+ for (Shell shell : shells) {
+ if (!shell.isDisposed()) shell.dispose();
+ }
+}
+
+public void disposeExec(Runnable runnable) {
+ checkDevice();
+ if (disposeList == null) disposeList = new Runnable[4];
+ for (int i = 0; i < disposeList.length; i++) {
+ if (disposeList[i] == null) {
+ disposeList[i] = runnable;
+ return;
+ }
+ }
+ Runnable[] newList = new Runnable[disposeList.length + 4];
+ System.arraycopy(disposeList, 0, newList, 0, disposeList.length);
+ newList[disposeList.length] = runnable;
+ disposeList = newList;
+}
+
+public static Display findDisplay(Thread thread) {
+ synchronized (Device.class) {
+ if (Default != null && Default.thread == thread) {
+ return Default;
+ }
+ return null;
+ }
+}
+
+public Widget findWidget(long handle) {
+ checkDevice();
+ return null;
+}
+
+public Widget findWidget(long handle, long id) {
+ checkDevice();
+ return null;
+}
+
+public Widget findWidget(Widget widget, long id) {
+ checkDevice();
+ return null;
+}
+
+public Shell getActiveShell() {
+ checkDevice();
+ return activeShell;
+}
+
+public static String getAppName() {
+ return APP_NAME;
+}
+
+public static String getAppVersion() {
+ return APP_VERSION;
+}
+
+public Rectangle getBounds() {
+ checkDevice();
+ return new Rectangle(0, 0, 1024, 768);
+}
+
+public Rectangle getClientArea() {
+ checkDevice();
+ return new Rectangle(0, 0, 1024, 768);
+}
+
+public static Display getCurrent() {
+ return findDisplay(Thread.currentThread());
+}
+
+public Control getCursorControl() {
+ checkDevice();
+ return null;
+}
+
+public Point getCursorLocation() {
+ checkDevice();
+ return new Point(0, 0);
+}
+
+public Point[] getCursorSizes() {
+ checkDevice();
+ return new Point[] { new Point(16, 16), new Point(32, 32) };
+}
+
+public Object getData(String key) {
+ checkDevice();
+ if (key == null) error(SWT.ERROR_NULL_ARGUMENT);
+ return super.getData(key);
+}
+
+public Object getData() {
+ checkDevice();
+ return super.getData();
+}
+
+public static Display getDefault() {
+ synchronized (Device.class) {
+ if (Default == null) Default = new Display();
+ return Default;
+ }
+}
+
+public int getDismissalAlignment() {
+ checkDevice();
+ return SWT.LEFT;
+}
+
+public int getDoubleClickTime() {
+ checkDevice();
+ return 500;
+}
+
+public Control getFocusControl() {
+ checkDevice();
+ return focusControl;
+}
+
+public boolean getHighContrast() {
+ checkDevice();
+ return false;
+}
+
+public int getDepth() {
+ return 24;
+}
+
+public int getIconDepth() {
+ return 24;
+}
+
+public Point[] getIconSizes() {
+ checkDevice();
+ return new Point[] { new Point(16, 16), new Point(32, 32) };
+}
+
+public Menu getMenuBar() {
+ checkDevice();
+ return null;
+}
+
+public Monitor[] getMonitors() {
+ checkDevice();
+ Monitor monitor = new Monitor();
+ Rectangle bounds = new Rectangle(0, 0, 1024, 768);
+ monitor.handle = 0;
+ monitor.x = bounds.x;
+ monitor.y = bounds.y;
+ monitor.width = bounds.width;
+ monitor.height = bounds.height;
+ monitor.clientX = bounds.x;
+ monitor.clientY = bounds.y;
+ monitor.clientWidth = bounds.width;
+ monitor.clientHeight = bounds.height;
+ monitor.zoom = 100;
+ return new Monitor[] { monitor };
+}
+
+public Monitor getPrimaryMonitor() {
+ checkDevice();
+ return getMonitors()[0];
+}
+
+public Shell[] getShells() {
+ checkDevice();
+ return shells;
+}
+
+public Thread getSyncThread() {
+ synchronized (Device.class) {
+ if (isDisposed()) error(SWT.ERROR_DEVICE_DISPOSED);
+ return synchronizer.syncThread;
+ }
+}
+
+@Override
+public Font getSystemFont() {
+ checkDevice();
+ return systemFont;
+}
+
+public Thread getThread() {
+ synchronized (Device.class) {
+ if (isDisposed()) error(SWT.ERROR_DEVICE_DISPOSED);
+ return thread;
+ }
+}
+
+public static boolean isSystemDarkTheme() {
+ return false;
+}
+
+public static boolean isHeadless() {
+ return true;
+}
+
+public boolean readAndDispatch() {
+ checkDevice();
+ return runAsyncMessages(false);
+}
+
+@Override
+protected void release() {
+ sendEvent(SWT.Dispose, new Event());
+ Shell[] shells = getShells();
+ for (Shell shell : shells) {
+ if (!shell.isDisposed()) shell.dispose();
+ }
+ if (eventTable != null) eventTable.unhook(SWT.Dispose, null);
+ super.release();
+ releaseDisplay();
+}
+
+void releaseDisplay() {
+ if (disposeList != null) {
+ for (Runnable runnable : disposeList) {
+ if (runnable != null) {
+ try {
+ runnable.run();
+ } catch (RuntimeException exception) {
+ runtimeExceptionHandler.accept(exception);
+ } catch (Error error) {
+ errorHandler.accept(error);
+ }
+ }
+ }
+ }
+ disposeList = null;
+ synchronizer.releaseSynchronizer();
+ synchronizer = null;
+ thread = null;
+ widgetTable = null;
+}
+
+void removeWidget(long handle) {
+ // No-op in headless mode
+}
+
+public void removeFilter(int eventType, Listener listener) {
+ checkDevice();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (filterTable == null) return;
+ filterTable.unhook(eventType, listener);
+ if (filterTable.size() == 0) filterTable = null;
+}
+
+public void removeListener(int eventType, Listener listener) {
+ checkDevice();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) return;
+ eventTable.unhook(eventType, listener);
+}
+
+boolean runAsyncMessages(boolean all) {
+ return synchronizer.runAsyncMessages(all);
+}
+
+boolean runDeferredLayouts() {
+ if (layoutDeferredCount != 0) {
+ Composite[] temp = layoutDeferred;
+ int count = layoutDeferredCount;
+ layoutDeferred = null;
+ layoutDeferredCount = 0;
+ for (int i = 0; i < count; i++) {
+ Composite comp = temp[i];
+ if (!comp.isDisposed()) comp.setLayoutDeferred(false);
+ }
+ return true;
+ }
+ return false;
+}
+
+boolean runDeferredEvents() {
+ boolean run = false;
+ if (runDeferredLayouts()) run = true;
+ return run;
+}
+
+void sendEvent(int eventType, Event event) {
+ if (eventTable == null && filterTable == null) return;
+ if (event == null) event = new Event();
+ event.display = this;
+ event.type = eventType;
+ if (event.time == 0) event.time = (int) System.currentTimeMillis();
+ if (filterTable != null) filterTable.sendEvent(event);
+ if (eventTable != null) eventTable.sendEvent(event);
+}
+
+public static void setAppName(String name) {
+ APP_NAME = name;
+}
+
+public static void setAppVersion(String version) {
+ APP_VERSION = version;
+}
+
+public void setData(String key, Object value) {
+ checkDevice();
+ if (key == null) error(SWT.ERROR_NULL_ARGUMENT);
+ super.setData(key, value);
+}
+
+public void setData(Object data) {
+ checkDevice();
+ super.setData(data);
+}
+
+public void setRuntimeExceptionHandler(Consumer handler) {
+ checkDevice();
+ if (handler == null) error(SWT.ERROR_NULL_ARGUMENT);
+ runtimeExceptionHandler = handler;
+}
+
+public void setErrorHandler(Consumer handler) {
+ checkDevice();
+ if (handler == null) error(SWT.ERROR_NULL_ARGUMENT);
+ errorHandler = handler;
+}
+
+public boolean sleep() {
+ checkDevice();
+ return runAsyncMessages(false) || runDeferredEvents();
+}
+
+public void syncExec(Runnable runnable) {
+ synchronized (Device.class) {
+ if (isDisposed()) error(SWT.ERROR_DEVICE_DISPOSED);
+ synchronizer.syncExec(runnable);
+ }
+}
+
+public int timerExec(int milliseconds, Runnable runnable) {
+ checkDevice();
+ if (runnable == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (timerList == null) {
+ timerList = new Runnable[4];
+ timerIds = new int[4];
+ }
+ int timerId = nextTimerId++;
+ int index = 0;
+ while (index < timerList.length) {
+ if (timerList[index] == null) break;
+ index++;
+ }
+ if (index == timerList.length) {
+ Runnable[] newTimerList = new Runnable[timerList.length + 4];
+ System.arraycopy(timerList, 0, newTimerList, 0, timerList.length);
+ timerList = newTimerList;
+ int[] newTimerIds = new int[timerIds.length + 4];
+ System.arraycopy(timerIds, 0, newTimerIds, 0, timerIds.length);
+ timerIds = newTimerIds;
+ }
+ timerList[index] = runnable;
+ timerIds[index] = timerId;
+
+ // Schedule the timer to run
+ new Timer().schedule(new TimerTask() {
+ @Override
+ public void run() {
+ asyncExec(runnable);
+ }
+ }, milliseconds);
+
+ return timerId;
+}
+
+public void wake() {
+ synchronized (Device.class) {
+ if (isDisposed()) error(SWT.ERROR_DEVICE_DISPOSED);
+ // No-op in headless mode
+ }
+}
+
+void addShell(Shell shell) {
+ int length = shells.length;
+ Shell[] newShells = new Shell[length + 1];
+ System.arraycopy(shells, 0, newShells, 0, length);
+ newShells[length] = shell;
+ shells = newShells;
+}
+
+void removeShell(Shell shell) {
+ int length = shells.length;
+ for (int i = 0; i < length; i++) {
+ if (shells[i] == shell) {
+ Shell[] newShells = new Shell[length - 1];
+ System.arraycopy(shells, 0, newShells, 0, i);
+ System.arraycopy(shells, i + 1, newShells, i, length - i - 1);
+ shells = newShells;
+ break;
+ }
+ }
+}
+
+void addLayoutDeferred(Composite comp) {
+ if (layoutDeferred == null) layoutDeferred = new Composite[64];
+ if (layoutDeferredCount == layoutDeferred.length) {
+ Composite[] temp = new Composite[layoutDeferred.length + 64];
+ System.arraycopy(layoutDeferred, 0, temp, 0, layoutDeferred.length);
+ layoutDeferred = temp;
+ }
+ layoutDeferred[layoutDeferredCount++] = comp;
+}
+
+void removeLayoutDeferred(Composite comp) {
+ if (layoutDeferred == null) return;
+ for (int i = 0; i < layoutDeferredCount; i++) {
+ if (layoutDeferred[i] == comp) {
+ layoutDeferredCount--;
+ System.arraycopy(layoutDeferred, i + 1, layoutDeferred, i, layoutDeferredCount - i);
+ layoutDeferred[layoutDeferredCount] = null;
+ break;
+ }
+ }
+}
+
+boolean filterEvent(Event event) {
+ if (filterTable != null) {
+ filterTable.sendEvent(event);
+ }
+ return false;
+}
+
+boolean filters(int eventType) {
+ if (filterTable == null) return false;
+ return filterTable.hooks(eventType);
+}
+
+void postEvent(Event event) {
+ if (eventQueue == null) eventQueue = new Event[4];
+ int index = 0;
+ int length = eventQueue.length;
+ while (index < length) {
+ if (eventQueue[index] == null) break;
+ index++;
+ }
+ if (index == length) {
+ Event[] newQueue = new Event[length + 4];
+ System.arraycopy(eventQueue, 0, newQueue, 0, length);
+ eventQueue = newQueue;
+ }
+ eventQueue[index] = event;
+}
+
+static boolean isValidClass(Class> clazz) {
+ String name = clazz.getName();
+ int index = name.lastIndexOf('.');
+ return name.substring(0, index + 1).equals(PACKAGE_PREFIX);
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Item.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Item.java
new file mode 100644
index 00000000000..552791f9071
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Item.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Item for SWT.
+ */
+public abstract class Item extends Widget {
+ String text = "";
+ Image image;
+
+public Item(Widget parent, int style) {
+ super(parent, style);
+}
+
+public Image getImage() {
+ checkWidget();
+ return image;
+}
+
+public String getText() {
+ checkWidget();
+ return text;
+}
+
+public void setImage(Image image) {
+ checkWidget();
+ this.image = image;
+}
+
+public void setText(String string) {
+ checkWidget();
+ if (string == null) error(SWT.ERROR_NULL_ARGUMENT);
+ text = string;
+}
+
+@Override
+String getNameText() {
+ return getText();
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Label.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Label.java
new file mode 100644
index 00000000000..b8a14659b83
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Label.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+/**
+ * Headless implementation of Label for SWT.
+ */
+public class Label extends Control {
+ String text = "";
+ Image image;
+ int alignment = SWT.LEFT;
+
+public Label(Composite parent, int style) {
+ super(parent, checkStyle(style));
+}
+
+static int checkStyle(int style) {
+ style |= SWT.NO_FOCUS;
+ if ((style & SWT.SEPARATOR) != 0) {
+ style = checkBits(style, SWT.VERTICAL, SWT.HORIZONTAL, 0, 0, 0, 0);
+ return checkBits(style, SWT.SHADOW_OUT, SWT.SHADOW_IN, SWT.SHADOW_NONE, 0, 0, 0);
+ }
+ return checkBits(style, SWT.LEFT, SWT.CENTER, SWT.RIGHT, 0, 0, 0);
+}
+
+public int getAlignment() {
+ checkWidget();
+ if ((style & SWT.SEPARATOR) != 0) return 0;
+ return alignment;
+}
+
+public Image getImage() {
+ checkWidget();
+ return image;
+}
+
+public String getText() {
+ checkWidget();
+ return text;
+}
+
+public void setAlignment(int alignment) {
+ checkWidget();
+ if ((style & SWT.SEPARATOR) != 0) return;
+ if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return;
+ this.alignment = alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
+}
+
+public void setImage(Image image) {
+ checkWidget();
+ if ((style & SWT.SEPARATOR) != 0) return;
+ this.image = image;
+ this.text = "";
+}
+
+public void setText(String string) {
+ checkWidget();
+ if (string == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if ((style & SWT.SEPARATOR) != 0) return;
+ text = string;
+ this.image = null;
+}
+
+@Override
+String getNameText() {
+ return getText();
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Menu.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Menu.java
new file mode 100644
index 00000000000..ee5c3fd12c8
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Menu.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2025 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.widgets;
+
+import org.eclipse.swt.*;
+import java.util.*;
+
+/**
+ * Headless implementation of Menu for SWT.
+ */
+public class Menu extends Widget {
+ Decorations parent;
+ MenuItem[] items = new MenuItem[0];
+ int x, y;
+ boolean visible;
+
+public Menu(Control parent) {
+ this(parent, SWT.POP_UP);
+}
+
+public Menu(Control parent, int style) {
+ super(parent, checkStyle(style));
+}
+
+public Menu(Decorations parent, int style) {
+ super(parent, checkStyle(style));
+ this.parent = parent;
+}
+
+public Menu(Menu parentMenu) {
+ this(parentMenu, SWT.DROP_DOWN);
+}
+
+public Menu(Menu parentMenu, int style) {
+ super(parentMenu, checkStyle(style));
+}
+
+public Menu(MenuItem parentItem) {
+ this(parentItem.parent, SWT.DROP_DOWN);
+}
+
+static int checkStyle(int style) {
+ return checkBits(style, SWT.POP_UP, SWT.BAR, SWT.DROP_DOWN, 0, 0, 0);
+}
+
+public int getItemCount() {
+ checkWidget();
+ return items.length;
+}
+
+public MenuItem getItem(int index) {
+ checkWidget();
+ if (index < 0 || index >= items.length) error(SWT.ERROR_INVALID_RANGE);
+ return items[index];
+}
+
+public MenuItem[] getItems() {
+ checkWidget();
+ MenuItem[] result = new MenuItem[items.length];
+ System.arraycopy(items, 0, result, 0, items.length);
+ return result;
+}
+
+public Decorations getParent() {
+ checkWidget();
+ return parent;
+}
+
+public Shell getShell() {
+ checkWidget();
+ return parent != null ? parent.getShell() : null;
+}
+
+public boolean getVisible() {
+ checkWidget();
+ return visible;
+}
+
+public int indexOf(MenuItem item) {
+ checkWidget();
+ if (item == null) error(SWT.ERROR_NULL_ARGUMENT);
+ for (int i = 0; i < items.length; i++) {
+ if (items[i] == item) return i;
+ }
+ return -1;
+}
+
+public boolean isEnabled() {
+ checkWidget();
+ return (state & DISABLED) == 0;
+}
+
+public boolean isVisible() {
+ checkWidget();
+ return getVisible();
+}
+
+public void setEnabled(boolean enabled) {
+ checkWidget();
+ if (enabled) {
+ state &= ~DISABLED;
+ } else {
+ state |= DISABLED;
+ }
+}
+
+public void setLocation(int x, int y) {
+ checkWidget();
+ this.x = x;
+ this.y = y;
+}
+
+public void setLocation(org.eclipse.swt.graphics.Point location) {
+ checkWidget();
+ if (location == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setLocation(location.x, location.y);
+}
+
+public void setVisible(boolean visible) {
+ checkWidget();
+ this.visible = visible;
+}
+
+void addItem(MenuItem item) {
+ MenuItem[] newItems = new MenuItem[items.length + 1];
+ System.arraycopy(items, 0, newItems, 0, items.length);
+ newItems[items.length] = item;
+ items = newItems;
+}
+
+void removeItem(MenuItem item) {
+ List