summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/moe/nea/mossbar/Launch.java18
-rw-r--r--src/main/java/moe/nea/mossbar/concepts/Bar.java103
-rw-r--r--src/main/java/moe/nea/mossbar/concepts/Display.java146
-rw-r--r--src/main/java/moe/nea/mossbar/concepts/Scope.java27
-rw-r--r--src/main/java/moe/nea/mossbar/concepts/ScopeObject.java12
-rw-r--r--src/main/java/moe/nea/mossbar/concepts/ScopeObjectProxy.java16
-rw-r--r--src/main/java/moe/nea/mossbar/concepts/ShmBufferPool.java89
-rw-r--r--src/main/java/mossbar/extensions/java/lang/AutoCloseable/ScopeObjectCloseable.java25
-rw-r--r--src/main/java/mossbar/extensions/org/freedesktop/wayland/client/Proxy/ProxyExtension.java20
9 files changed, 456 insertions, 0 deletions
diff --git a/src/main/java/moe/nea/mossbar/Launch.java b/src/main/java/moe/nea/mossbar/Launch.java
new file mode 100644
index 0000000..23d3868
--- /dev/null
+++ b/src/main/java/moe/nea/mossbar/Launch.java
@@ -0,0 +1,18 @@
+package moe.nea.mossbar;
+
+import moe.nea.mossbar.concepts.Bar;
+import moe.nea.mossbar.concepts.Display;
+import moe.nea.mossbar.concepts.Scope;
+
+public class Launch {
+ public static void main(String[] args) {
+ var rootScope = Scope.root();
+ var display = new Display().bindTo(rootScope);
+ var bar = new Bar(display, display.outputs.first).bindTo(rootScope);
+ bar.renderOnce();
+ while (display.proxy.dispatch() >= 0) {
+ }
+
+ rootScope.closeObject();
+ }
+}
diff --git a/src/main/java/moe/nea/mossbar/concepts/Bar.java b/src/main/java/moe/nea/mossbar/concepts/Bar.java
new file mode 100644
index 0000000..891b28a
--- /dev/null
+++ b/src/main/java/moe/nea/mossbar/concepts/Bar.java
@@ -0,0 +1,103 @@
+package moe.nea.mossbar.concepts;
+
+import org.freedesktop.wayland.client.*;
+import org.freedesktop.wayland.shared.WlOutputTransform;
+import org.freedesktop.wayland.shared.ZwlrLayerShellV1Layer;
+import org.freedesktop.wayland.shared.ZwlrLayerSurfaceV1Anchor;
+import org.freedesktop.wayland.util.EnumUtil;
+
+import java.io.IOException;
+import java.nio.IntBuffer;
+import java.util.EnumSet;
+
+public class Bar extends Scope {
+ private final Display display;
+ private final WlSurfaceProxy surface;
+ private final ZwlrLayerSurfaceV1Proxy layer;
+ private final ShmBufferPool bufferPool;
+ private WlCallbackProxy callbackProxy;
+
+
+ public Bar(Display display, WlOutputProxy output) {
+ this.display = display;
+ this.surface = display.compositorProxy.createSurface(new WlSurfaceEventsV6() {
+ @Override
+ public void enter(WlSurfaceProxy emitter, WlOutputProxy output) {
+ System.out.println("Surface entered");
+ }
+
+ @Override
+ public void leave(WlSurfaceProxy emitter, WlOutputProxy output) {
+ System.out.println("Left surface");
+ }
+
+ @Override
+ public void preferredBufferScale(WlSurfaceProxy emitter, int factor) {
+ System.out.println("Preferred buffer scale ${factor}");
+ }
+
+ @Override
+ public void preferredBufferTransform(WlSurfaceProxy emitter, int transform) {
+ var transforms = EnumUtil.decode(WlOutputTransform.class, transform);
+ System.out.println("Preferred buffer transform ${transforms}");
+ }
+ }).bindTo(this);
+ this.layer = display.layerShell.getLayerSurface(new ZwlrLayerSurfaceV1EventsV4() {
+ @Override
+ public void configure(ZwlrLayerSurfaceV1Proxy emitter, int serial, int width, int height) {
+ System.out.println("Configure layer ${serial} ${width}x${height}");
+ emitter.ackConfigure(serial);
+ }
+
+ @Override
+ public void closed(ZwlrLayerSurfaceV1Proxy emitter) {
+ System.out.println("layer closed");
+ }
+ }, this.surface, output, ZwlrLayerShellV1Layer.OVERLAY.getValue(), "mossbar")
+ .bindTo(this);
+ int width = 1920;
+ int height = 30;
+ layer.setSize(width, height); // TODO??? this should really not be a hardcoded width
+ layer.setAnchor(EnumUtil.encode(EnumSet.of(ZwlrLayerSurfaceV1Anchor.TOP, ZwlrLayerSurfaceV1Anchor.RIGHT, ZwlrLayerSurfaceV1Anchor.LEFT)));
+ surface.commit();
+ try {
+ bufferPool = ShmBufferPool.newARGBPool(display, width, height, 2);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ display.roundtrip();
+ }
+
+ public void renderOnce() {
+ System.out.println("Rendering");
+ var nextBuffer = bufferPool.poll();
+ assert nextBuffer != null;
+ var pixels = nextBuffer.getByteBuffer().asIntBuffer();
+ fill(pixels, 0xFFFF00A0);
+ surface.attach(nextBuffer.getProxy(), 0, 0);
+ surface.damage(0, 0, nextBuffer.width, nextBuffer.height);
+ queueNextFrame();
+ surface.commit();
+ System.out.println("surface committed");
+ }
+
+ private void queueNextFrame() {
+ if (callbackProxy != null)
+ callbackProxy.destroy();
+ callbackProxy = surface.frame((_, _) -> renderOnce());
+ }
+
+ @Override
+ public void closeObject() {
+ callbackProxy.destroy();
+ super.closeObject();
+ }
+
+ private static void fill(IntBuffer buffer, int value) {
+ buffer.clear();
+ buffer.limit(buffer.capacity());
+ while (buffer.hasRemaining()) {
+ buffer.put(value);
+ }
+ }
+}
diff --git a/src/main/java/moe/nea/mossbar/concepts/Display.java b/src/main/java/moe/nea/mossbar/concepts/Display.java
new file mode 100644
index 0000000..0234739
--- /dev/null
+++ b/src/main/java/moe/nea/mossbar/concepts/Display.java
@@ -0,0 +1,146 @@
+package moe.nea.mossbar.concepts;
+
+import manifold.ext.props.rt.api.PropOption;
+import manifold.ext.props.rt.api.get;
+import manifold.ext.props.rt.api.set;
+import org.freedesktop.wayland.client.*;
+import org.freedesktop.wayland.shared.WlSeatCapability;
+import org.freedesktop.wayland.util.EnumUtil;
+import org.jspecify.annotations.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class Display extends Scope {
+ private final WlDisplayProxy proxy;
+ private final WlRegistryProxy registry;
+ private int shmFormat = 0;
+ private WlCompositorProxy compositorProxy;
+ private final List<WlOutputProxy> outputs = new ArrayList<>();
+
+ public List<WlOutputProxy> getOutputs() {
+ return outputs;
+ }
+
+ public WlCompositorProxy getCompositorProxy() {
+ return compositorProxy;
+ }
+
+ @get
+ @set(PropOption.Private)
+ WlShmProxy shmProxy;
+ @get
+ @set(PropOption.Private)
+ WlSeatProxy seatProxy;
+ @get
+ @set(PropOption.Private)
+ XdgWmBaseProxy xdgWmBaseProxy;
+ @get
+ @set(PropOption.Private)
+ ZwlrLayerShellV1Proxy layerShell;
+
+ public Display() {
+ proxy = WlDisplayProxy.connect(Objects.requireNonNull(System.getenv("WAYLAND_DISPLAY"), "No WAYLAND_DISPLAY found"));
+ registry = proxy.getRegistry(new WlRegistryEvents() {
+ @Override
+ public void global(WlRegistryProxy emitter, int name, @NonNull String interfaceName, int version) {
+ System.out.println("Receiving interface " + interfaceName + "." + version);
+ registerInterface(name, interfaceName);
+ }
+
+ @Override
+ public void globalRemove(WlRegistryProxy emitter, int name) {
+ System.out.println("Removed " + name);
+ }
+ });
+
+ proxy.roundtrip();
+ proxy.roundtrip();
+
+ Objects.requireNonNull(layerShell);
+ Objects.requireNonNull(compositorProxy);
+ Objects.requireNonNull(xdgWmBaseProxy);
+ Objects.requireNonNull(seatProxy);
+ if (outputs.isEmpty) // TODO: dynamically find more outputs
+ throw new IllegalStateException("No outputs found");
+ }
+
+
+ private <J, T extends Proxy<J>> T bind(int name, Class<T> proxyType, int version, J implementation) {
+ var proxy = registry
+ .bind(name, proxyType, version, implementation);
+ proxy.bindTo(this);
+ return proxy;
+ }
+
+ private void registerInterface(int name, String interfaceName) {
+ switch (interfaceName) {
+ case WlCompositorProxy.INTERFACE_NAME ->
+ Display.this.compositorProxy = bind(name, WlCompositorProxy.class, WlCompositorEventsV6.VERSION, new WlCompositorEventsV6() {
+ });
+ case WlShmProxy.INTERFACE_NAME ->
+ Display.this.shmProxy = bind(name, WlShmProxy.class, WlShmEvents.VERSION, new WlShmEvents() {
+ @Override
+ public void format(WlShmProxy emitter, int format) {
+ Display.this.shmFormat |= (1 << format);
+ }
+ });
+ case XdgWmBaseProxy.INTERFACE_NAME ->
+ Display.this.xdgWmBaseProxy = bind(name, XdgWmBaseProxy.class, XdgWmBaseEventsV5.VERSION, new XdgWmBaseEventsV5() {
+ @Override
+ public void ping(XdgWmBaseProxy emitter, int serial) {
+ emitter.pong(serial);
+ }
+ });
+ case ZwlrLayerShellV1Proxy.INTERFACE_NAME ->
+ Display.this.layerShell = bind(name, ZwlrLayerShellV1Proxy.class, ZwlrLayerShellV1EventsV4.VERSION, new ZwlrLayerShellV1EventsV4() {
+ });
+ case WlOutputProxy.INTERFACE_NAME ->
+ outputs.add(bind(name, WlOutputProxy.class, WlOutputEvents.VERSION, new WlOutputEvents() {
+ @Override
+ public void geometry(WlOutputProxy emitter, int x, int y, int physicalWidth, int physicalHeight, int subpixel, String make, String model, int transform) {
+
+ }
+
+ @Override
+ public void mode(WlOutputProxy emitter, int flags, int width, int height, int refresh) {
+
+ }
+ }));
+ case WlSeatProxy.INTERFACE_NAME ->
+ Display.this.seatProxy = bind(name, WlSeatProxy.class, WlSeatEventsV3.VERSION, new WlSeatEventsV3() {
+ @Override
+ public void capabilities(WlSeatProxy emitter, int capabilities) {
+ var caps = EnumUtil.decode(WlSeatCapability.class, capabilities);
+ System.out.println("capabilities: " + caps);
+ }
+
+ @Override
+ public void name(WlSeatProxy emitter, @NonNull String name) {
+ System.out.println("Obtained seat " + name);
+ }
+ });
+ }
+ }
+
+ public WlDisplayProxy getProxy() {
+ return proxy;
+ }
+
+ @Override
+ public void closeObject() {
+ super.closeObject();
+ System.out.println("Destroying registry");
+ registry.destroy();
+ System.out.println("Destroyed registry");
+ proxy.flush();
+ System.out.println("Flushed proxy");
+ proxy.disconnect();
+ System.out.println("Destroyed proxy");
+ }
+
+ public void roundtrip() {
+ proxy.roundtrip();
+ }
+}
diff --git a/src/main/java/moe/nea/mossbar/concepts/Scope.java b/src/main/java/moe/nea/mossbar/concepts/Scope.java
new file mode 100644
index 0000000..68c5cc1
--- /dev/null
+++ b/src/main/java/moe/nea/mossbar/concepts/Scope.java
@@ -0,0 +1,27 @@
+package moe.nea.mossbar.concepts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class Scope implements ScopeObject {
+ List<ScopeObject> scopedObjects = new ArrayList<>();
+
+ public static Scope root() {
+ return new Scope() {
+ };
+ }
+
+ protected <T extends ScopeObject> T bind(T scopeObject) {
+ scopedObjects.add(scopeObject);
+ return scopeObject;
+ }
+
+ @Override
+ public void closeObject() {
+ for (int i = scopedObjects.size() - 1; i >= 0; i--) {
+ ScopeObject scopeObject = scopedObjects.get(i);
+ scopeObject.closeObject();
+ }
+ }
+
+}
diff --git a/src/main/java/moe/nea/mossbar/concepts/ScopeObject.java b/src/main/java/moe/nea/mossbar/concepts/ScopeObject.java
new file mode 100644
index 0000000..4c59f3b
--- /dev/null
+++ b/src/main/java/moe/nea/mossbar/concepts/ScopeObject.java
@@ -0,0 +1,12 @@
+package moe.nea.mossbar.concepts;
+
+import manifold.ext.rt.api.Self;
+
+public interface ScopeObject {
+
+ default @Self ScopeObject bindTo(Scope scope) {
+ return scope.bind(this);
+ }
+
+ void closeObject();
+}
diff --git a/src/main/java/moe/nea/mossbar/concepts/ScopeObjectProxy.java b/src/main/java/moe/nea/mossbar/concepts/ScopeObjectProxy.java
new file mode 100644
index 0000000..6bf96a2
--- /dev/null
+++ b/src/main/java/moe/nea/mossbar/concepts/ScopeObjectProxy.java
@@ -0,0 +1,16 @@
+package moe.nea.mossbar.concepts;
+
+import org.freedesktop.wayland.client.Proxy;
+
+public class ScopeObjectProxy implements ScopeObject {
+ private final Proxy<?> proxy;
+
+ public ScopeObjectProxy(Proxy<?> proxy) {
+ this.proxy = proxy;
+ }
+
+ @Override
+ public void closeObject() {
+ proxy.destroy();
+ }
+}
diff --git a/src/main/java/moe/nea/mossbar/concepts/ShmBufferPool.java b/src/main/java/moe/nea/mossbar/concepts/ShmBufferPool.java
new file mode 100644
index 0000000..f7cd636
--- /dev/null
+++ b/src/main/java/moe/nea/mossbar/concepts/ShmBufferPool.java
@@ -0,0 +1,89 @@
+package moe.nea.mossbar.concepts;
+
+import manifold.ext.props.rt.api.get;
+import org.freedesktop.wayland.client.WlBufferEvents;
+import org.freedesktop.wayland.client.WlBufferProxy;
+import org.freedesktop.wayland.client.WlShmPoolEvents;
+import org.freedesktop.wayland.shared.WlShmFormat;
+import org.freedesktop.wayland.util.ShmPool;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+public class ShmBufferPool extends Scope {
+ private boolean destroyed = false;
+ private Queue<Buffer> ready = new ArrayDeque<>();
+
+ void enqueue(Buffer buffer) {
+ requireNotDestroyed();
+ ready.add(buffer);
+ }
+
+ public class Buffer extends Scope implements WlBufferEvents, WlShmPoolEvents {
+ private final WlBufferProxy bufferProxy;
+ private final ShmPool shmPool;
+ @get
+ final int width;
+ @get
+ final int height;
+ @get
+ final int byteWidth;
+ @get
+ final @NonNull WlShmFormat format;
+
+ Buffer(Display display, int width, int height, int byteWidth, WlShmFormat format) throws IOException {
+ this.width = width;
+ this.height = height;
+ this.byteWidth = byteWidth;
+ this.format = format;
+ int bufferSize = width * height * byteWidth;
+ shmPool = new ShmPool(bufferSize).bindTo(this); // TODO: shouldnt this be a long upstream? maybe wayland is just 32 bit like that
+ var poolProxy = display.shmProxy.createPool(this, shmPool.fd, bufferSize);
+ bufferProxy = poolProxy.createBuffer(this, 0, width, height, width * byteWidth, format.value).bindTo(this);
+ poolProxy.destroy();
+ }
+
+ public ByteBuffer getByteBuffer() {
+ return shmPool.asByteBuffer();
+ }
+
+ @Override
+ public void release(WlBufferProxy emitter) {
+ enqueue(this);
+ }
+
+ public WlBufferProxy getProxy() {
+ return bufferProxy;
+ }
+ }
+
+ private void requireNotDestroyed() {
+ if (destroyed)
+ throw new IllegalStateException("pool destroyed");
+ }
+
+ public @Nullable Buffer poll() {
+ requireNotDestroyed();
+ return ready.poll();
+ }
+
+ @Override
+ public void closeObject() {
+ super.closeObject();
+ destroyed = true;
+ }
+
+ public ShmBufferPool(Display display, int width, int height, int bufferCount, int byteWidth, WlShmFormat shmFormat) throws IOException {
+ for (int i = 0; i < bufferCount; i++) {
+ ready.add(new Buffer(display, width, height, byteWidth, shmFormat).bindTo(this));
+ }
+ }
+
+ public static ShmBufferPool newARGBPool(Display display, int width, int height, int bufferCount) throws IOException {
+ return new ShmBufferPool(display, width, height, bufferCount, 4, WlShmFormat.ARGB8888);
+ }
+}
diff --git a/src/main/java/mossbar/extensions/java/lang/AutoCloseable/ScopeObjectCloseable.java b/src/main/java/mossbar/extensions/java/lang/AutoCloseable/ScopeObjectCloseable.java
new file mode 100644
index 0000000..6268327
--- /dev/null
+++ b/src/main/java/mossbar/extensions/java/lang/AutoCloseable/ScopeObjectCloseable.java
@@ -0,0 +1,25 @@
+package mossbar.extensions.java.lang.AutoCloseable;
+
+import manifold.ext.rt.api.Extension;
+import manifold.ext.rt.api.Self;
+import manifold.ext.rt.api.This;
+import moe.nea.mossbar.concepts.Scope;
+import moe.nea.mossbar.concepts.ScopeObject;
+
+@Extension
+public class ScopeObjectCloseable {
+ public static ScopeObject asScopeObject(@This AutoCloseable closeable) {
+ return () -> {
+ try {
+ closeable.close();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ };
+ }
+
+ public static @Self AutoCloseable bindTo(@Self @This AutoCloseable thiz, Scope scope) {
+ thiz.asScopeObject().bindTo(scope);
+ return thiz;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/mossbar/extensions/org/freedesktop/wayland/client/Proxy/ProxyExtension.java b/src/main/java/mossbar/extensions/org/freedesktop/wayland/client/Proxy/ProxyExtension.java
new file mode 100644
index 0000000..50aee26
--- /dev/null
+++ b/src/main/java/mossbar/extensions/org/freedesktop/wayland/client/Proxy/ProxyExtension.java
@@ -0,0 +1,20 @@
+package mossbar.extensions.org.freedesktop.wayland.client.Proxy;
+
+import manifold.ext.rt.api.Extension;
+import manifold.ext.rt.api.Self;
+import manifold.ext.rt.api.This;
+import moe.nea.mossbar.concepts.Scope;
+import moe.nea.mossbar.concepts.ScopeObjectProxy;
+import org.freedesktop.wayland.client.Proxy;
+
+@Extension
+public class ProxyExtension {
+ public static <I> ScopeObjectProxy asScopeObject(@This Proxy<I> thiz) {
+ return new ScopeObjectProxy(thiz);
+ }
+
+ public static <I> @Self Proxy<I> bindTo(@Self @This Proxy<I> thiz, Scope scope) {
+ asScopeObject(thiz).bindTo(scope);
+ return thiz;
+ }
+} \ No newline at end of file