diff options
Diffstat (limited to 'src')
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 |
