Skip to content

Commit

Permalink
Adding defaultGC and updating readme and ITs.
Browse files Browse the repository at this point in the history
  • Loading branch information
moaxcp committed Dec 30, 2020
1 parent a09ed28 commit a59f6fe
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 10 deletions.
141 changes: 134 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ x11-client enables java and other jvm languages to talk directly to a x11
server without binding to a C library. The client is similar to X11lib for C
but uses objects to represent the protocol resulting in a simplified client. It
supports the core protocol and the following extensions: bigreq, composite,
damage, dpms, dri2, ge, record, render, res, screensaver, shape, sync, xc_misc,
xevie, xf86dri, xf86vidmode, xfixes, xinerama, xprint, xselinux, xtest. The
client is similar to X11lib and follows the same pattern of queuing one-way
requests before sending them to the server.
damage, dpms, dri2, ge, randr, record, render, res, screensaver, shape, shm,
sync, xc_misc, xevie, xf86dri, xf86vidmode, xfixes, xinerama, xprint, xselinux,
xtest, xv, xvmc. The client is similar to X11lib and follows the same pattern
of queuing one-way requests before sending them to the server.

![Java CI with Gradle](https://github.com/moaxcp/x11-client/workflows/Java%20CI%20with%20Gradle/badge.svg?branch=master)
[![Java CI with Gradle](https://github.com/moaxcp/x11-client/workflows/Java%20CI%20with%20Gradle/badge.svg?branch=master)](https://github.com/moaxcp/x11-client/actions?query=workflow%3A%22Java+CI+with+Gradle%22)
[![maven central](https://img.shields.io/maven-central/v/com.github.moaxcp.x11/x11-client)](https://search.maven.org/artifact/com.github.moaxcp.x11/x11-client)
[![javadoc](https://javadoc.io/badge2/com.github.moaxcp.x11/x11-client/javadoc.svg)](https://javadoc.io/doc/com.github.moaxcp.x11/x11-client)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=com.github.moaxcp.x11%3Ax11-client&metric=alert_status)](https://sonarcloud.io/dashboard?id=com.github.moaxcp.x11%3Ax11-client)
Expand Down Expand Up @@ -145,6 +145,9 @@ These examples are conversions of a X11lib example written in C.

## Hello World

This is an example of a simple window. In this example none of the helper
methods are used. Raw requests are built and sent directly to the server.

```
try(X11Client x11Client = X11Client.connect()) {
CreateWindow window = CreateWindow.builder()
Expand Down Expand Up @@ -191,7 +194,6 @@ try(X11Client x11Client = X11Client.connect()) {
x11Client.send(ImageText8.builder()
.drawable(window.getWid())
.gc(gc.getCid())
.stringLen((byte) "Hello World!".length())
.string(stringToByteList("Hello World!"))
.x((short) 10)
.y((short) 50)
Expand All @@ -203,6 +205,116 @@ try(X11Client x11Client = X11Client.connect()) {
}
```

The next example is the same window but uses helper methods in the client.

```
try(X11Client client = X11Client.connect()) {
int wid = client.createSimpleWindow((short) 10, (short) 10, (short) 600, (short) 480, EventMask.EXPOSURE, EventMask.KEY_PRESS);
client.storeName(wid, "Hello World!");
int deleteAtom = client.getAtom("WM_DELETE_WINDOW");
client.setWMProtocols(wid, deleteAtom);
client.mapWindow(wid);
int gc = client.createGC(0, wid);
while(true) {
XEvent event = client.getNextEvent();
if(event instanceof ExposeEvent) {
client.fillRectangle(wid, gc, (short) 20, (short) 20, (short) 10, (short) 10);
client.drawString(wid, gc, (short) 10, (short) 50, "Hello World!");
} else if(event instanceof KeyPressEvent) {
break;
} else if(event instanceof ClientMessageEvent) {
ClientMessageEvent clientMessage = (ClientMessageEvent) event;
if(clientMessage.getFormat() == 32) {
ClientMessageData32 data = (ClientMessageData32) clientMessage.getData();
if(data.getData32().get(0) == deleteAtom) {
break;
}
}
}
}
}
```

## TinyWM

[TinyWM](http://incise.org/tinywm.html) is a famous small window manager
written in around 50 lines of code. This example is the implementation in java.

```
try(X11Client client = X11Client.connect(new DisplayName(":1"))) {
int wid = client.createSimpleWindow(10, 10, 200, 200);
client.mapWindow(wid);
client.send(GrabKey.builder()
.key((byte) client.keySymToKeyCode(KeySym.getByName("F1").get().getValue()))
.modifiersEnable(ModMask.ONE)
.grabWindow(client.getRoot(0))
.ownerEvents(true)
.keyboardMode(GrabMode.ASYNC)
.pointerMode(GrabMode.ASYNC)
.build());
client.send(GrabButton.builder()
.button(ButtonIndex.ONE)
.modifiersEnable(ModMask.ONE)
.grabWindow(client.getRoot(0))
.ownerEvents(true)
.eventMaskEnable(BUTTON_PRESS, BUTTON_RELEASE, POINTER_MOTION)
.keyboardMode(GrabMode.ASYNC)
.pointerMode(GrabMode.ASYNC)
.build());
client.send(GrabButton.builder()
.button(ButtonIndex.THREE)
.modifiersEnable(ModMask.ONE)
.grabWindow(client.getRoot(0))
.ownerEvents(true)
.eventMaskEnable(BUTTON_PRESS, BUTTON_RELEASE, POINTER_MOTION)
.keyboardMode(GrabMode.ASYNC)
.pointerMode(GrabMode.ASYNC)
.build());
GetGeometryReply geometry = null;
ButtonPressEvent start = null;
while(true) {
XEvent event = client.getNextEvent();
if(event instanceof KeyPressEvent) {
KeyPressEvent keyPress = (KeyPressEvent) event;
int child = keyPress.getChild();
if(child != Window.NONE.getValue()) {
client.raiseWindow(child);
}
} else if(event instanceof ButtonPressEvent) {
ButtonPressEvent buttonPress = (ButtonPressEvent) event;
int child = buttonPress.getChild();
if(child != Window.NONE.getValue()) {
geometry = client.send(GetGeometry.builder()
.drawable(child)
.build());
start = buttonPress;
}
} else if(event instanceof MotionNotifyEvent) {
MotionNotifyEvent motionNotify = (MotionNotifyEvent) event;
int child = motionNotify.getChild();
if(child != Window.NONE.getValue()) {
int xdiff = motionNotify.getRootX() - start.getRootX();
int ydiff = motionNotify.getRootY() - start.getRootY();
client.send(ConfigureWindow.builder()
.window(child)
.x(geometry.getX() + (start.getDetail() == ButtonIndex.ONE.getValue() ? xdiff : 0))
.y(geometry.getY() + (start.getDetail() == ButtonIndex.ONE.getValue() ? ydiff : 0))
.width(Math.max(1, geometry.getWidth() + (start.getDetail() == ButtonIndex.THREE.getValue() ? xdiff : 0)))
.height(Math.max(1, geometry.getHeight() + (start.getDetail() == ButtonIndex.THREE.getValue() ? ydiff : 0)))
.build());
}
} else if(event instanceof ButtonReleaseEvent) {
start = null;
}
}
}
```

The java version is a bit longer due to the builder pattern being a little more
verbose.

# Design

## Request Prossesing
Expand Down Expand Up @@ -320,15 +432,30 @@ and will likely move into a new project.

Removing length properties from protocol objects where it is simply the list
size. The length still needs to be set for properties involving complex
expressions.
expressions. This results in not having to set the length of lists on most
objects. For example drawing a string no longer requires the size.

```
client.send(ImageText8.builder()
.drawable(window.getWid())
.gc(gc.getCid())
.string(stringToByteList("Hello World!"))
.x((short) 10)
.y((short) 50)
.build());
```

Adding exclude to x11protocol plugin.

Fixing issues with objects missing padding for the first field. This enables
the sync extension to work.

Added support for file descriptors. They are simply treated as an `int`.

Added extensions shm, sync xrandr, xv, and xvmc

Added defaultGC cache for root windows and method to automatically create them.

## 0.3.0

Adding TinyWM example.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
public class X11ClientIT {

@Test
void simpleHelloWorld() throws IOException {
void simpleHelloWorldMouse() throws IOException {
try(X11Client client = X11Client.connect()) {
CreateWindow window = CreateWindow.builder()
.depth(client.getDepth(0))
Expand Down Expand Up @@ -121,6 +121,64 @@ void simpleHelloWorld() throws IOException {
}
}

@Test
void simpleHelloWorld() throws IOException {
try(X11Client x11Client = X11Client.connect()) {
CreateWindow window = CreateWindow.builder()
.depth(x11Client.getDepth(0))
.wid(x11Client.nextResourceId())
.parent(x11Client.getRoot(0))
.x((short) 10)
.y((short) 10)
.width((short) 600)
.height((short) 480)
.borderWidth((short) 5)
.clazz(WindowClass.COPY_FROM_PARENT)
.visual(x11Client.getVisualId(0))
.backgroundPixel(x11Client.getWhitePixel(0))
.borderPixel(x11Client.getBlackPixel(0))
.eventMaskEnable(EventMask.EXPOSURE, EventMask.KEY_PRESS)
.build();
x11Client.send(window);
x11Client.send(MapWindow.builder()
.window(window.getWid())
.build());
CreateGC gc = CreateGC.builder()
.cid(x11Client.nextResourceId())
.drawable(window.getWid())
.background(x11Client.getWhitePixel(0))
.foreground(x11Client.getBlackPixel(0))
.build();
x11Client.send(gc);
while(true) {
XEvent event = x11Client.getNextEvent();
if(event instanceof ExposeEvent) {
List<Rectangle> rectangles = new ArrayList<>();
rectangles.add(Rectangle.builder()
.x((short) 20)
.y((short) 20)
.width((short) 10)
.height((short) 10)
.build());
x11Client.send(PolyFillRectangle.builder()
.drawable(window.getWid())
.gc(gc.getCid())
.rectangles(rectangles)
.build());
x11Client.send(ImageText8.builder()
.drawable(window.getWid())
.gc(gc.getCid())
.string(stringToByteList("Hello World!"))
.x((short) 10)
.y((short) 50)
.build());
} else if(event instanceof KeyPressEvent) {
break;
}
}
}
}

@Test
void clientTestXFunctions() throws IOException {
try(X11Client client = X11Client.connect()) {
Expand All @@ -145,8 +203,6 @@ void clientTestXFunctions() throws IOException {
break;
}
}
} else {
throw new IllegalStateException(event.toString());
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/github/moaxcp/x11client/X11Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import lombok.NonNull;

import static com.github.moaxcp.x11client.protocol.Utilities.byteArrayToList;
Expand All @@ -18,6 +20,7 @@ public class X11Client implements AutoCloseable {
private final XProtocolService protocolService;
private final ResourceIdService resourceIdService;
private final AtomService atomService;
private final Map<Integer, Integer> defaultGCs = new HashMap<>();

/**
* Creates a client for the given displayName and authority.
Expand Down Expand Up @@ -119,6 +122,7 @@ public void flush() {
*/
@Override
public void close() throws IOException {
defaultGCs.values().stream().forEach(i -> send(FreeGC.builder().gc(i).build()));
connection.close();
}

Expand Down Expand Up @@ -186,6 +190,7 @@ public void storeName(int wid, String name) {
.property(Atom.WM_NAME.getValue())
.type(Atom.STRING.getValue())
.format((byte) 8)
.dataLen(name.length())
.data(stringToByteList(name))
.build());
}
Expand All @@ -198,6 +203,7 @@ public void setWMProtocols(int wid, int atom) {
.type(Atom.ATOM.getValue())
.format((byte) 32)
.mode(PropMode.REPLACE)
.dataLen(1)
.data(byteArrayToList(ByteBuffer.allocate(4).putInt(atom).array()))
.build());
}
Expand All @@ -219,6 +225,16 @@ public int createGC(int screen, int wid) {
return gc;
}

public int defaultGC(int screen) {
int root = getRoot(screen);
if(defaultGCs.containsKey(root)) {
return defaultGCs.get(root);
}
int gc = createGC(screen, root);
defaultGCs.put(root, gc);
return gc;
}

/**
* see https://github.com/mirror/libX11/blob/master/src/Text.c
* @param drawable
Expand Down

0 comments on commit a59f6fe

Please sign in to comment.