From c6313e392bee01c42a04e5a4fe2cfe723b93667f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Oct 2025 06:07:04 +0000
Subject: [PATCH 1/4] Initial plan
From 5aa3c474eb6a97e7d2a8610a76c59151696d9cf1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Oct 2025 06:22:25 +0000
Subject: [PATCH 2/4] Add Display.isHeadless() method and create headless
widget implementations
Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com>
---
.../org/eclipse/swt/widgets/Display.java | 17 +
.../gtk/org/eclipse/swt/widgets/Display.java | 17 +
.../org/eclipse/swt/widgets/Button.java | 141 ++++
.../org/eclipse/swt/widgets/Canvas.java | 58 ++
.../org/eclipse/swt/widgets/Composite.java | 209 ++++++
.../org/eclipse/swt/widgets/Control.java | 349 ++++++++++
.../org/eclipse/swt/widgets/Decorations.java | 110 ++++
.../org/eclipse/swt/widgets/Display.java | 605 ++++++++++++++++++
.../org/eclipse/swt/widgets/Label.java | 83 +++
.../org/eclipse/swt/widgets/Scrollable.java | 48 ++
.../org/eclipse/swt/widgets/Shell.java | 329 ++++++++++
.../org/eclipse/swt/widgets/Widget.java | 345 ++++++++++
.../org/eclipse/swt/widgets/Display.java | 17 +
13 files changed, 2328 insertions(+)
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Button.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Canvas.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Composite.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Control.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Decorations.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Display.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Label.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Scrollable.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Shell.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Widget.java
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/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/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/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/Scrollable.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Scrollable.java
new file mode 100644
index 00000000000..d81382dfda8
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Scrollable.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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 Scrollable for SWT.
+ */
+public abstract class Scrollable extends Control {
+ ScrollBar horizontalBar, verticalBar;
+
+public Scrollable() {
+ // No-op
+}
+
+public Scrollable(Composite parent, int style) {
+ super(parent, style);
+}
+
+public Rectangle getClientArea() {
+ checkWidget();
+ return new Rectangle(0, 0, width, height);
+}
+
+public ScrollBar getHorizontalBar() {
+ checkWidget();
+ return horizontalBar;
+}
+
+public ScrollBar getVerticalBar() {
+ checkWidget();
+ return verticalBar;
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Shell.java
new file mode 100644
index 00000000000..465fe127ba4
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Shell.java
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ * 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 Shell for SWT.
+ */
+public class Shell extends Decorations {
+ boolean opened;
+ String text = "";
+ Image image;
+ Image[] images = new Image[0];
+ int alpha = 255;
+ Region region;
+ int minWidth = 0, minHeight = 0;
+ int maxWidth = Integer.MAX_VALUE, maxHeight = Integer.MAX_VALUE;
+ boolean modified;
+ boolean fullScreen;
+
+public Shell() {
+ this((Display) null, SWT.SHELL_TRIM);
+}
+
+public Shell(int style) {
+ this((Display) null, style);
+}
+
+public Shell(Display display) {
+ this(display, SWT.SHELL_TRIM);
+}
+
+public Shell(Display display, int style) {
+ super(display, checkStyle(display, style));
+ if (display == null) display = Display.getCurrent();
+ if (display == null) display = Display.getDefault();
+ this.display = display;
+ createWidget();
+}
+
+public Shell(Shell parent) {
+ this(parent, SWT.DIALOG_TRIM);
+}
+
+public Shell(Shell parent, int style) {
+ super(parent, checkStyle(parent, style));
+ if (parent != null) {
+ this.display = parent.display;
+ if (this.parent == null) this.parent = parent;
+ }
+ createWidget();
+}
+
+void createWidget() {
+ display.addShell(this);
+ width = 200;
+ height = 200;
+}
+
+static int checkStyle(Display display, int style) {
+ style = checkBits(style, SWT.ON_TOP, SWT.TOOL, SWT.NO_TRIM, 0, 0, 0);
+ if ((style & (SWT.ON_TOP | SWT.TOOL)) != 0) {
+ if ((style & SWT.CLOSE) != 0) style |= SWT.TITLE;
+ }
+ if ((style & SWT.NO_TRIM) != 0) {
+ return style & ~(SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX | SWT.RESIZE | SWT.BORDER);
+ }
+ if ((style & (SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX | SWT.RESIZE | SWT.BORDER)) == 0) {
+ style |= SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX | SWT.RESIZE | SWT.BORDER;
+ }
+ return style;
+}
+
+static int checkStyle(Shell parent, int style) {
+ if ((style & SWT.ON_TOP) != 0) style &= ~SWT.SHELL_TRIM;
+ int mask = SWT.SYSTEM_MODAL | SWT.APPLICATION_MODAL | SWT.PRIMARY_MODAL;
+ if ((style & SWT.SHEET) != 0) {
+ style &= ~SWT.SHEET;
+ style |= (parent != null) ? SWT.DIALOG_TRIM : SWT.NONE;
+ if ((style & mask) == 0) {
+ style |= (parent != null) ? SWT.APPLICATION_MODAL : SWT.NONE;
+ }
+ }
+ int bits = style & ~mask;
+ if ((style & SWT.SYSTEM_MODAL) != 0) return bits | SWT.SYSTEM_MODAL;
+ if ((style & SWT.APPLICATION_MODAL) != 0) return bits | SWT.APPLICATION_MODAL;
+ if ((style & SWT.PRIMARY_MODAL) != 0) return bits | SWT.PRIMARY_MODAL;
+ return bits;
+}
+
+public void addShellListener(ShellListener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Close, typedListener);
+ addListener(SWT.Iconify, typedListener);
+ addListener(SWT.Deiconify, typedListener);
+ addListener(SWT.Activate, typedListener);
+ addListener(SWT.Deactivate, typedListener);
+}
+
+public void close() {
+ checkWidget();
+ Event event = new Event();
+ sendEvent(SWT.Close, event);
+ if (event.doit && !isDisposed()) dispose();
+}
+
+public void dispose() {
+ if (isDisposed()) return;
+ display.removeShell(this);
+ super.dispose();
+}
+
+public int getAlpha() {
+ checkWidget();
+ return alpha;
+}
+
+public Rectangle getBounds() {
+ checkWidget();
+ return new Rectangle(x, y, width, height);
+}
+
+public boolean getFullScreen() {
+ checkWidget();
+ return fullScreen;
+}
+
+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 boolean getMaximized() {
+ checkWidget();
+ return false;
+}
+
+public Point getMaximumSize() {
+ checkWidget();
+ return new Point(maxWidth, maxHeight);
+}
+
+public boolean getMinimized() {
+ checkWidget();
+ return false;
+}
+
+public Point getMinimumSize() {
+ checkWidget();
+ return new Point(minWidth, minHeight);
+}
+
+public boolean getModified() {
+ checkWidget();
+ return modified;
+}
+
+public Region getRegion() {
+ checkWidget();
+ return region;
+}
+
+@Override
+public Shell getShell() {
+ checkWidget();
+ return this;
+}
+
+public Shell[] getShells() {
+ checkWidget();
+ int count = 0;
+ Shell[] allShells = display.getShells();
+ for (Shell shell : allShells) {
+ Control control = shell;
+ while (control != null && control != this) {
+ control = control.parent;
+ }
+ if (control == this) count++;
+ }
+ int index = 0;
+ Shell[] result = new Shell[count];
+ for (Shell shell : allShells) {
+ Control control = shell;
+ while (control != null && control != this) {
+ control = control.parent;
+ }
+ if (control == this) {
+ result[index++] = shell;
+ }
+ }
+ return result;
+}
+
+public String getText() {
+ checkWidget();
+ return text;
+}
+
+public void open() {
+ checkWidget();
+ setVisible(true);
+ opened = true;
+}
+
+public void removeShellListener(ShellListener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) return;
+ eventTable.unhook(SWT.Close, listener);
+ eventTable.unhook(SWT.Iconify, listener);
+ eventTable.unhook(SWT.Deiconify, listener);
+ eventTable.unhook(SWT.Activate, listener);
+ eventTable.unhook(SWT.Deactivate, listener);
+}
+
+public void setActive() {
+ checkWidget();
+ display.activeShell = this;
+}
+
+public void setAlpha(int alpha) {
+ checkWidget();
+ this.alpha = alpha;
+}
+
+public void setEnabled(boolean enabled) {
+ checkWidget();
+ this.enabled = enabled;
+}
+
+public void setFullScreen(boolean fullScreen) {
+ checkWidget();
+ this.fullScreen = fullScreen;
+}
+
+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 setMaximized(boolean maximized) {
+ checkWidget();
+ // No-op in headless mode
+}
+
+public void setMaximumSize(int width, int height) {
+ checkWidget();
+ maxWidth = width;
+ maxHeight = height;
+}
+
+public void setMaximumSize(Point size) {
+ checkWidget();
+ if (size == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setMaximumSize(size.x, size.y);
+}
+
+public void setMinimized(boolean minimized) {
+ checkWidget();
+ // No-op in headless mode
+}
+
+public void setMinimumSize(int width, int height) {
+ checkWidget();
+ minWidth = width;
+ minHeight = height;
+}
+
+public void setMinimumSize(Point size) {
+ checkWidget();
+ if (size == null) error(SWT.ERROR_NULL_ARGUMENT);
+ setMinimumSize(size.x, size.y);
+}
+
+public void setModified(boolean modified) {
+ checkWidget();
+ this.modified = modified;
+}
+
+public void setRegion(Region region) {
+ checkWidget();
+ this.region = region;
+}
+
+public void setText(String string) {
+ checkWidget();
+ if (string == null) error(SWT.ERROR_NULL_ARGUMENT);
+ text = string;
+}
+
+@Override
+protected void checkSubclass() {
+ // Allow subclassing
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Widget.java
new file mode 100644
index 00000000000..fd6a5c15a9f
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Widget.java
@@ -0,0 +1,345 @@
+/*******************************************************************************
+ * 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 Widget for SWT.
+ */
+public abstract class Widget {
+ public long handle;
+ int style, state;
+ Display display;
+ EventTable eventTable;
+ Object data;
+
+ /* Global state flags */
+ static final int DISPOSED = 1<<0;
+ static final int CANVAS = 1<<1;
+ static final int KEYED_DATA = 1<<2;
+ static final int HANDLE = 1<<3;
+ static final int DISABLED = 1<<4;
+ static final int MENU = 1<<5;
+ static final int OBSCURED = 1<<6;
+ static final int MOVED = 1<<7;
+ static final int RESIZED = 1<<8;
+ static final int ZERO_WIDTH = 1<<9;
+ static final int ZERO_HEIGHT = 1<<10;
+ static final int HIDDEN = 1<<11;
+ static final int FOREGROUND = 1<<12;
+ static final int BACKGROUND = 1<<13;
+ static final int FONT = 1<<14;
+ static final int PARENT_BACKGROUND = 1<<15;
+ static final int THEME_BACKGROUND = 1<<16;
+ static final int LAYOUT_NEEDED = 1<<17;
+ static final int LAYOUT_CHANGED = 1<<18;
+ static final int LAYOUT_CHILD = 1<<19;
+ static final int RELEASED = 1<<20;
+ static final int DISPOSE_SENT = 1<<21;
+ static final int FOREIGN_HANDLE = 1<<22;
+ static final int DRAG_DETECT = 1<<23;
+ static final int SKIN_NEEDED = 1<<24;
+
+ /* Default sizes */
+ static final int DEFAULT_WIDTH = 64;
+ static final int DEFAULT_HEIGHT = 64;
+
+public Widget() {
+ // No-op
+}
+
+public Widget(Widget parent, int style) {
+ checkSubclass();
+ checkParent(parent);
+ this.style = style;
+ display = parent.display;
+}
+
+protected void checkSubclass() {
+ if (!isValidSubclass()) error(SWT.ERROR_INVALID_SUBCLASS);
+}
+
+protected void checkParent(Widget parent) {
+ if (parent == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (parent.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
+ if (parent.display != display) error(SWT.ERROR_INVALID_PARENT);
+}
+
+protected void checkWidget() {
+ Display display = this.display;
+ if (display == null) error(SWT.ERROR_WIDGET_DISPOSED);
+ if (display.thread != Thread.currentThread()) error(SWT.ERROR_THREAD_INVALID_ACCESS);
+ if ((state & DISPOSED) != 0) error(SWT.ERROR_WIDGET_DISPOSED);
+}
+
+static int checkBits(int style, int int0, int int1, int int2, int int3, int int4, int int5) {
+ int mask = int0 | int1 | int2 | int3 | int4 | int5;
+ if ((style & mask) == 0) style |= int0;
+ if ((style & int0) != 0) style = (style & ~mask) | int0;
+ if ((style & int1) != 0) style = (style & ~mask) | int1;
+ if ((style & int2) != 0) style = (style & ~mask) | int2;
+ if ((style & int3) != 0) style = (style & ~mask) | int3;
+ if ((style & int4) != 0) style = (style & ~mask) | int4;
+ if ((style & int5) != 0) style = (style & ~mask) | int5;
+ return style;
+}
+
+public void addListener(int eventType, Listener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ _addListener(eventType, listener);
+}
+
+void _addListener(int eventType, Listener listener) {
+ if (eventTable == null) eventTable = new EventTable();
+ eventTable.hook(eventType, listener);
+}
+
+public void addDisposeListener(DisposeListener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Dispose, typedListener);
+}
+
+public void dispose() {
+ if (isDisposed()) return;
+ if (!isValidThread()) error(SWT.ERROR_THREAD_INVALID_ACCESS);
+ release(true);
+}
+
+void error(int code) {
+ SWT.error(code);
+}
+
+public Object getData() {
+ checkWidget();
+ return (state & KEYED_DATA) != 0 ? ((Object[]) data)[0] : data;
+}
+
+public Object getData(String key) {
+ checkWidget();
+ if (key == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if ((state & KEYED_DATA) != 0) {
+ Object[] table = (Object[]) data;
+ for (int i = 1; i < table.length; i += 2) {
+ if (key.equals(table[i])) return table[i + 1];
+ }
+ }
+ return null;
+}
+
+public Display getDisplay() {
+ Display display = this.display;
+ if (display == null) error(SWT.ERROR_WIDGET_DISPOSED);
+ return display;
+}
+
+public int getStyle() {
+ checkWidget();
+ return style;
+}
+
+public boolean isDisposed() {
+ return (state & DISPOSED) != 0;
+}
+
+public boolean isListening(int eventType) {
+ checkWidget();
+ return eventTable != null && eventTable.hooks(eventType);
+}
+
+boolean isValidSubclass() {
+ return Display.isValidClass(getClass());
+}
+
+boolean isValidThread() {
+ return getDisplay().thread == Thread.currentThread();
+}
+
+public void notifyListeners(int eventType, Event event) {
+ checkWidget();
+ if (event == null) event = new Event();
+ sendEvent(eventType, event);
+}
+
+void postEvent(int eventType) {
+ sendEvent(eventType, null, false);
+}
+
+void postEvent(int eventType, Event event) {
+ sendEvent(eventType, event, false);
+}
+
+void release(boolean destroy) {
+ if ((state & DISPOSE_SENT) == 0) {
+ state |= DISPOSE_SENT;
+ sendEvent(SWT.Dispose);
+ }
+ if ((state & DISPOSED) == 0) {
+ releaseChildren(destroy);
+ }
+ if ((state & RELEASED) == 0) {
+ state |= RELEASED;
+ if (destroy) {
+ releaseParent();
+ releaseWidget();
+ }
+ }
+}
+
+void releaseChildren(boolean destroy) {
+ // No-op
+}
+
+void releaseHandle() {
+ handle = 0;
+ state |= DISPOSED;
+}
+
+void releaseParent() {
+ // No-op
+}
+
+void releaseWidget() {
+ eventTable = null;
+ data = null;
+}
+
+public void removeListener(int eventType, Listener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) return;
+ eventTable.unhook(eventType, listener);
+}
+
+public void removeDisposeListener(DisposeListener listener) {
+ checkWidget();
+ if (listener == null) error(SWT.ERROR_NULL_ARGUMENT);
+ if (eventTable == null) return;
+ eventTable.unhook(SWT.Dispose, listener);
+}
+
+void sendEvent(Event event) {
+ Display display = event.display = this.display;
+ if (!display.filterEvent(event)) {
+ if (eventTable != null) eventTable.sendEvent(event);
+ }
+}
+
+void sendEvent(int eventType) {
+ sendEvent(eventType, null, true);
+}
+
+void sendEvent(int eventType, Event event) {
+ sendEvent(eventType, event, true);
+}
+
+void sendEvent(int eventType, Event event, boolean send) {
+ if (eventTable == null && !display.filters(eventType)) {
+ return;
+ }
+ if (event == null) event = new Event();
+ event.type = eventType;
+ event.display = display;
+ event.widget = this;
+ if (event.time == 0) {
+ event.time = (int) System.currentTimeMillis();
+ }
+ if (send) {
+ sendEvent(event);
+ } else {
+ display.postEvent(event);
+ }
+}
+
+public void setData(Object data) {
+ checkWidget();
+ if ((state & KEYED_DATA) != 0) {
+ ((Object[]) this.data)[0] = data;
+ } else {
+ this.data = data;
+ }
+}
+
+public void setData(String key, Object value) {
+ checkWidget();
+ if (key == null) error(SWT.ERROR_NULL_ARGUMENT);
+ int index = 1;
+ Object[] table = null;
+ if ((state & KEYED_DATA) != 0) {
+ table = (Object[]) data;
+ while (index < table.length) {
+ if (key.equals(table[index])) break;
+ index += 2;
+ }
+ }
+ if (value != null) {
+ if ((state & KEYED_DATA) != 0) {
+ if (index == table.length) {
+ Object[] newTable = new Object[table.length + 2];
+ System.arraycopy(table, 0, newTable, 0, table.length);
+ data = table = newTable;
+ }
+ } else {
+ table = new Object[3];
+ table[0] = data;
+ data = table;
+ state |= KEYED_DATA;
+ }
+ table[index] = key;
+ table[index + 1] = value;
+ } else {
+ if ((state & KEYED_DATA) != 0) {
+ if (index != table.length) {
+ int length = table.length - 2;
+ if (length == 1) {
+ data = table[0];
+ state &= ~KEYED_DATA;
+ } else {
+ Object[] newTable = new Object[length];
+ System.arraycopy(table, 0, newTable, 0, index);
+ System.arraycopy(table, index + 2, newTable, index, length - index);
+ data = newTable;
+ }
+ }
+ }
+ }
+}
+
+@Override
+public String toString() {
+ String string = "*Disposed*";
+ if (!isDisposed()) {
+ string = "*Wrong Thread*";
+ if (isValidThread()) string = getNameText();
+ }
+ return getName() + " {" + string + "}";
+}
+
+String getName() {
+ String string = getClass().getName();
+ int index = string.lastIndexOf('.');
+ if (index == -1) return string;
+ return string.substring(index + 1, string.length());
+}
+
+String getNameText() {
+ return "";
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java
index 12ed1c2f9d5..5f04b065275 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java
@@ -2187,6 +2187,23 @@ public static boolean isSystemDarkTheme () {
return isDarkTheme;
}
+/**
+ * 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 OS.GetMessageTime ();
}
From d2ec7b0dddc3f17fd594b6899b67c29ce1cb67c3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 4 Oct 2025 06:26:07 +0000
Subject: [PATCH 3/4] Create org.eclipse.swt.headless fragment and supporting
classes
Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com>
---
binaries/org.eclipse.swt.headless/.project | 87 ++++++++
.../META-INF/MANIFEST.MF | 25 +++
binaries/org.eclipse.swt.headless/README.md | 211 ++++++++++++++++++
.../org.eclipse.swt.headless/build.properties | 29 +++
.../forceQualifierUpdate.txt | 2 +
.../fragment.properties | 12 +
binaries/pom.xml | 1 +
.../org/eclipse/swt/widgets/Caret.java | 129 +++++++++++
.../org/eclipse/swt/widgets/Item.java | 56 +++++
.../org/eclipse/swt/widgets/Menu.java | 148 ++++++++++++
.../org/eclipse/swt/widgets/MenuItem.java | 115 ++++++++++
.../org/eclipse/swt/widgets/ScrollBar.java | 164 ++++++++++++++
12 files changed, 979 insertions(+)
create mode 100644 binaries/org.eclipse.swt.headless/.project
create mode 100644 binaries/org.eclipse.swt.headless/META-INF/MANIFEST.MF
create mode 100644 binaries/org.eclipse.swt.headless/README.md
create mode 100644 binaries/org.eclipse.swt.headless/build.properties
create mode 100644 binaries/org.eclipse.swt.headless/forceQualifierUpdate.txt
create mode 100644 binaries/org.eclipse.swt.headless/fragment.properties
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Caret.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Item.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/Menu.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/MenuItem.java
create mode 100644 bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/ScrollBar.java
diff --git a/binaries/org.eclipse.swt.headless/.project b/binaries/org.eclipse.swt.headless/.project
new file mode 100644
index 00000000000..7fe95cb6c47
--- /dev/null
+++ b/binaries/org.eclipse.swt.headless/.project
@@ -0,0 +1,87 @@
+
+
+ org.eclipse.swt.headless
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+ org.eclipse.pde.api.tools.apiAnalysisBuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.pde.PluginNature
+ org.eclipse.pde.api.tools.apiAnalysisNature
+
+
+
+ Eclipse SWT
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT
+
+
+ Eclipse SWT Accessibility
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20Accessibility
+
+
+ Eclipse SWT AWT
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20AWT
+
+
+ Eclipse SWT Browser
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20Browser
+
+
+ Eclipse SWT Custom Widgets
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20Custom%20Widgets
+
+
+ Eclipse SWT Drag and Drop
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20Drag%20and%20Drop
+
+
+ Eclipse SWT OpenGL
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20OpenGL
+
+
+ Eclipse SWT Printing
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20Printing
+
+
+ Eclipse SWT Program
+ 2
+ SWT_HOST_PLUGIN/Eclipse%20SWT%20Program
+
+
+
+
+ SWT_HOST_PLUGIN
+ $%7BPARENT-2-PROJECT_LOC%7D/bundles/org.eclipse.swt
+
+
+
diff --git a/binaries/org.eclipse.swt.headless/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.headless/META-INF/MANIFEST.MF
new file mode 100644
index 00000000000..5362e503eb7
--- /dev/null
+++ b/binaries/org.eclipse.swt.headless/META-INF/MANIFEST.MF
@@ -0,0 +1,25 @@
+Manifest-Version: 1.0
+Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)"
+Bundle-Name: %fragmentName
+Bundle-Vendor: %providerName
+Bundle-SymbolicName: org.eclipse.swt.headless; singleton:=true
+Bundle-Version: 3.132.0.qualifier
+Bundle-ManifestVersion: 2
+Bundle-Localization: fragment
+Export-Package:
+ org.eclipse.swt,
+ org.eclipse.swt.accessibility,
+ org.eclipse.swt.awt,
+ org.eclipse.swt.browser,
+ org.eclipse.swt.custom,
+ org.eclipse.swt.dnd,
+ org.eclipse.swt.events,
+ org.eclipse.swt.graphics,
+ org.eclipse.swt.layout,
+ org.eclipse.swt.opengl,
+ org.eclipse.swt.printing,
+ org.eclipse.swt.program,
+ org.eclipse.swt.widgets,
+ org.eclipse.swt.internal; x-friends:="org.eclipse.ui",
+ org.eclipse.swt.internal.image; x-internal:=true
+Automatic-Module-Name: org.eclipse.swt.headless
diff --git a/binaries/org.eclipse.swt.headless/README.md b/binaries/org.eclipse.swt.headless/README.md
new file mode 100644
index 00000000000..c66dcc66456
--- /dev/null
+++ b/binaries/org.eclipse.swt.headless/README.md
@@ -0,0 +1,211 @@
+# SWT Headless Fragment
+
+## Overview
+
+The `org.eclipse.swt.headless` fragment provides a headless implementation of SWT (Standard Widget Toolkit) that allows applications to run without requiring a native windowing system or display. This is similar to Java AWT's headless mode (`java.awt.headless`).
+
+## Purpose
+
+The headless fragment is useful for:
+
+- **Server-side applications** that need to reference UI code but don't display a UI
+- **Testing** UI applications without requiring a display
+- **Continuous Integration** environments where no display is available
+- **Headless rendering** of UI layouts for documentation or screenshots
+- **Command-line tools** that use SWT libraries but don't require actual UI display
+
+## Key Features
+
+### Detection
+
+You can check if SWT is running in headless mode:
+
+```java
+if (Display.isHeadless()) {
+ // Running in headless mode
+}
+```
+
+### Behavior
+
+In headless mode:
+
+- **Widget creation**: Widgets can be created and configured normally
+- **State management**: Widget properties (text, selection, bounds, etc.) are stored and can be retrieved
+- **Layout**: Layout managers work normally and calculate sizes
+- **Parent-child relationships**: Composite widgets maintain their child lists correctly
+- **Events**: Event listeners can be registered (though events are not automatically triggered)
+- **No rendering**: No actual native widgets are created, no drawing occurs
+- **No user interaction**: Mouse and keyboard events don't occur naturally
+
+## Supported Widgets
+
+The headless implementation provides the following core widgets:
+
+### Containers
+- `Display` - The main display object
+- `Shell` - Top-level window
+- `Composite` - Container for other controls
+- `Canvas` - Drawing surface (no actual drawing)
+- `Decorations` - Base for decorated containers
+
+### Controls
+- `Button` - Push button, checkbox, radio, toggle, arrow
+- `Label` - Text or image label
+- `Control` - Base class for all controls
+
+### Supporting Classes
+- `Widget` - Base class for all widgets
+- `Scrollable` - Base for scrollable controls
+- `Menu` - Menu bar and popup menus
+- `MenuItem` - Individual menu items
+- `Item` - Base for items
+- `Caret` - Text cursor
+- `ScrollBar` - Scrollbar widget
+
+## Limitations
+
+The headless implementation has the following limitations:
+
+### Not Implemented
+
+The following features are **not** currently implemented:
+
+1. **Native rendering**: No actual drawing to screen or images
+2. **User input**: No keyboard or mouse events
+3. **System integration**:
+ - No system tray
+ - No clipboard
+ - No drag and drop
+ - No native file/color/font dialogs
+4. **Advanced widgets**: Many complex widgets are not yet implemented:
+ - Table, Tree, List
+ - Text, StyledText
+ - Browser
+ - And many others
+5. **Graphics operations**: GC (Graphics Context) drawing operations are no-ops
+6. **Images**: Image loading and manipulation is limited
+7. **Fonts and colors**: System fonts and colors return defaults
+
+### Default Behavior
+
+- `Display.getDefault()` creates a headless display
+- `Display.isHeadless()` returns `true`
+- Widget methods that can't be implemented return default values:
+ - `computeSize()` returns reasonable defaults (64x64 or based on content)
+ - `getBounds()` returns stored bounds
+ - System queries return sensible defaults
+- Operations that require native resources are no-ops:
+ - `redraw()` does nothing
+ - `update()` does nothing
+ - Event loops don't wait for user input
+
+## Usage Example
+
+```java
+// Create a headless display
+Display display = new Display();
+System.out.println("Headless: " + Display.isHeadless()); // true
+
+// Create a shell
+Shell shell = new Shell(display);
+shell.setText("Test Shell");
+shell.setSize(400, 300);
+
+// Create controls
+Composite composite = new Composite(shell, SWT.NONE);
+composite.setLayout(new FillLayout());
+
+Button button = new Button(composite, SWT.PUSH);
+button.setText("Click Me");
+
+Label label = new Label(composite, SWT.NONE);
+label.setText("Hello Headless SWT!");
+
+// Layout works normally
+shell.layout();
+
+// Can query widget properties
+System.out.println("Button text: " + button.getText());
+System.out.println("Label text: " + label.getText());
+System.out.println("Shell size: " + shell.getSize());
+
+// Cleanup
+shell.dispose();
+display.dispose();
+```
+
+## Installation
+
+The headless fragment is automatically available when:
+
+1. The `org.eclipse.swt` bundle is present
+2. The `org.eclipse.swt.headless` fragment is in the classpath
+3. No platform-specific fragment (gtk, cocoa, win32) is available
+
+The OSGi framework will automatically select the headless fragment when no native platform is available.
+
+## Building
+
+The headless fragment is built as part of the normal SWT build process:
+
+```bash
+mvn clean install
+```
+
+The fragment is located in `binaries/org.eclipse.swt.headless/`.
+
+## Contributing
+
+When extending the headless implementation:
+
+1. **Keep it simple**: Headless implementations should be minimal
+2. **Store and return**: Store values set via setters, return them from getters
+3. **No-op when necessary**: Operations that require native resources should be no-ops
+4. **Parent-child tracking**: Maintain widget hierarchies properly
+5. **Follow patterns**: Look at existing headless widgets for consistency
+
+### Adding New Widgets
+
+To add a new widget to the headless implementation:
+
+1. Create the widget class in `bundles/org.eclipse.swt/Eclipse SWT/headless/org/eclipse/swt/widgets/`
+2. Implement the public API from the platform-specific versions
+3. Store state in private fields
+4. Handle parent-child relationships for Composite widgets
+5. Return sensible defaults for queries
+
+## Testing
+
+The headless implementation can be tested using JUnit tests that don't require a display:
+
+```java
+@Test
+public void testHeadlessButton() {
+ Display display = new Display();
+ assertTrue(Display.isHeadless());
+
+ Shell shell = new Shell(display);
+ Button button = new Button(shell, SWT.PUSH);
+ button.setText("Test");
+
+ assertEquals("Test", button.getText());
+ assertFalse(button.getSelection());
+
+ button.setSelection(true);
+ assertTrue(button.getSelection());
+
+ shell.dispose();
+ display.dispose();
+}
+```
+
+## See Also
+
+- [Java AWT Headless Mode](https://www.oracle.com/technical-resources/articles/javase/headless.html)
+- [SWT API Documentation](https://help.eclipse.org/latest/index.jsp)
+- [Eclipse SWT Project](https://www.eclipse.org/swt/)
+
+## License
+
+Eclipse Public License 2.0 (EPL-2.0)
diff --git a/binaries/org.eclipse.swt.headless/build.properties b/binaries/org.eclipse.swt.headless/build.properties
new file mode 100644
index 00000000000..cd19887ad25
--- /dev/null
+++ b/binaries/org.eclipse.swt.headless/build.properties
@@ -0,0 +1,29 @@
+###############################################################################
+# Copyright (c) 2025 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+custom = true
+bin.includes = .,fragment.properties
+source.. = \
+ ../../bundles/org.eclipse.swt/Eclipse SWT/headless,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT/emulated/bidi,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT/emulated/coolbar,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT/emulated/taskbar,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT/emulated/tooltip,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT/emulated/expand,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT Accessibility/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT AWT/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT Printing/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT Program/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT Browser/common,\
+ ../../bundles/org.eclipse.swt/Eclipse SWT OpenGL/common
+output.. = bin/
diff --git a/binaries/org.eclipse.swt.headless/forceQualifierUpdate.txt b/binaries/org.eclipse.swt.headless/forceQualifierUpdate.txt
new file mode 100644
index 00000000000..c656da0edbc
--- /dev/null
+++ b/binaries/org.eclipse.swt.headless/forceQualifierUpdate.txt
@@ -0,0 +1,2 @@
+# To force a version qualifier update add the bug here
+Bug 577498 - Initial headless implementation
diff --git a/binaries/org.eclipse.swt.headless/fragment.properties b/binaries/org.eclipse.swt.headless/fragment.properties
new file mode 100644
index 00000000000..33896261db6
--- /dev/null
+++ b/binaries/org.eclipse.swt.headless/fragment.properties
@@ -0,0 +1,12 @@
+###############################################################################
+# Copyright (c) 2025 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+fragmentName = Standard Widget Toolkit Headless Implementation
+providerName = Eclipse.org
diff --git a/binaries/pom.xml b/binaries/pom.xml
index 78946f0f1b8..09d839e6372 100644
--- a/binaries/pom.xml
+++ b/binaries/pom.xml
@@ -40,6 +40,7 @@
org.eclipse.swt.gtk.linux.x86_64
org.eclipse.swt.win32.win32.aarch64
org.eclipse.swt.win32.win32.x86_64
+ org.eclipse.swt.headless
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/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/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