All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.moaxcp.x11.x11client.XProtocolService Maven / Gradle / Ivy

There is a newer version: 0.18.2
Show newest version
package com.github.moaxcp.x11.x11client;

import com.github.moaxcp.x11.protocol.*;
import com.github.moaxcp.x11.protocol.bigreq.Enable;
import com.github.moaxcp.x11.protocol.xproto.QueryExtension;
import com.github.moaxcp.x11.protocol.xproto.Setup;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Queue;

import static com.github.moaxcp.x11.protocol.Utilities.toX11Input;

public class XProtocolService {
  private final X11Input in;
  private final X11Output out;
  private final Setup setup;
  private int nextSequenceNumber = 1;
  private long maximumRequestLength;
  private final Queue requests = new LinkedList<>();
  private final Queue events = new LinkedList<>();
  private final ProtocolPluginService pluginService;

  public XProtocolService(Setup setup, X11Input in, X11Output out) {
    this.in = in;
    this.out = out;
    this.setup = setup;
    maximumRequestLength = setup.getMaximumRequestLength();
    pluginService = new ProtocolPluginService();
    for (String name : pluginService.listLoadedPlugins()) {
      activatePlugin(name);
    }

    if (pluginService.activatedPlugin("BIG-REQUESTS")) {
      maximumRequestLength = Integer.toUnsignedLong(send(Enable.builder().build())
              .getMaximumRequestLength());
    }
  }

  public Setup getSetup() {
    return this.setup;
  }

  public int getNextSequenceNumber() {
    return this.nextSequenceNumber;
  }

  public long getMaximumRequestLength() {
    return this.maximumRequestLength;
  }

  public boolean activatePlugin(String name) {
    return pluginService.loadedPlugin(name)
            .flatMap(XProtocolPlugin::getExtensionXName)
            .map(xName -> QueryExtension.builder()
                    .name(Utilities.toByteList(xName))
                    .build())
            .map(this::send)
            .map(reply -> reply.isPresent() && pluginService.activatePlugin(name, reply.getMajorOpcode(), reply.getFirstEvent(), reply.getFirstError()))
            .orElse(false);
  }

  public boolean loadedPlugin(String name) {
    return pluginService.listLoadedPlugins().contains(name);
  }

  public boolean activatedPlugin(String name) {
    return pluginService.listActivatedPlugins().contains(name);
  }

  public  T send(TwoWayRequest request) {
    flush();
    actuallySend(request);
    return readReply(request.getReplyFunction());
  }

  public void send(OneWayRequest request) {
    requests.add(request);
  }

  private void actuallySend(XRequest request) {
    try {
      request.write(getMajorOpcode(request), out);
    } catch (IOException e) {
      throw new X11ProtocolException("exception writing request \"" + request + "\"", e);
    }
    nextSequenceNumber++;
  }

  public byte getMajorOpcode(XRequest request) {
    return pluginService.majorOpcodeForRequest(request);
  }

  public  T readReply(XReplyFunction function) {
    while (true) {
      try {
        byte responseCode = in.readByte();
        if (responseCode == 0) {
          throw new X11ErrorException(readError());
        } else if (responseCode == 1) {
          byte field = in.readCard8();
          short sequenceNumber = in.readCard16();
          return function.get(field, sequenceNumber, in);
        } else {
          events.add(readEvent(responseCode));
        }
      } catch (IOException e) {
        throw new X11ProtocolException("exception when reading reply", e);
      }
    }
  }

  public  T readReply(X11Input in, XReplyFunction function) {
    try {
      byte responseCode = in.readByte();
      if (responseCode == 1) {
        byte field = in.readCard8();
        short sequenceNumber = in.readCard16();
        return function.get(field, sequenceNumber, in);
      } else {
        throw new X11ProtocolException("expected responseCode 1 but was " + responseCode);
      }
    } catch (IOException e) {
      throw new X11ProtocolException("exception when reading reply", e);
    }
  }

  private  T readError() throws IOException {
    return readError(in);
  }

  public  T readError(X11Input in) {
    try {
      byte code = in.readCard8();
      XProtocolPlugin plugin = pluginService.activePluginForError(code).orElseThrow(() -> new IllegalStateException(XProtocolPlugin.class.getSimpleName() + " not found for error code " + code));
      return plugin.readError(code, in);
    } catch (IOException e) {
      throw new X11ProtocolException("could not read error", e);
    }
  }

  private  T readEvent(byte responseCode) throws IOException {
    return readEvent(in, responseCode);
  }

  private  T readEvent(X11Input in, byte responseCode) throws IOException {
    boolean sentEvent = responseCode < 0;
    byte number = !sentEvent ? responseCode : (byte) (responseCode ^ (byte) 0b10000000);
    if (pluginService.genericEventNumber(number)) {
      byte extension = in.readCard8();
      Optional pluginExtension = pluginService.activePluginForMajorOpcode(extension);
      if (!pluginExtension.isPresent()) {
        throw new IllegalStateException("Could not find plugin for generic event with major-opcode of " + extension);
      }
      XProtocolPlugin plugin = pluginExtension.get();

      short sequenceNumber = in.readCard16();
      int length = in.readCard32();
      short eventType = in.readCard16();
      return plugin.readGenericEvent(sentEvent, extension, sequenceNumber, length, eventType, in);
    }
    XProtocolPlugin plugin = pluginService.activePluginForEvent(number)
            .orElseThrow(() -> new IllegalStateException(XProtocolPlugin.class.getSimpleName() + " not found for number " + number));
    return plugin.readEvent(number, sentEvent, in);
  }

  public  T getNextEvent() {
    if (!events.isEmpty()) {
      return (T) events.poll();
    }
    flush();
    try {
      byte responseCode = in.readByte();
      if (responseCode == 0) {
        throw new X11ErrorException(readError());
      }
      if (responseCode == 1) {
        throw new X11ProtocolException("reply not expected when reading events");
      }
      return readEvent(responseCode);
    } catch (IOException e) {
      throw new X11ProtocolException("exception when reading event", e);
    }
  }

  public void flush() {
    while (!requests.isEmpty()) {
      XRequest request = requests.poll();
      actuallySend(request);
    }
  }

  public void discard() {
    events.clear();
  }

  public  T readRequest(X11Input in) throws IOException {
    byte majorOpcode = in.readByte();
    byte minorOpcode = in.peekByte();

    Optional pluginOptional = pluginService.activePluginFor(majorOpcode, minorOpcode);
    if (!pluginOptional.isPresent()) {
      throw new IllegalStateException("Could not find plugin for request with major opcode of " + majorOpcode);
    }
    XProtocolPlugin plugin = pluginOptional.get();
    return plugin.readRequest(majorOpcode, minorOpcode, in);
  }

  public  T readEvent(X11Input in) {
    try {
      byte responseCode = in.readByte();
      return readEvent(in, responseCode);
    } catch (IOException e) {
      throw new X11ProtocolException("exception when reading event", e);
    }
  }

  public  T readError(List bytes) {
    X11Input in = toX11Input(bytes);
    try {
      byte responseCode = in.readByte();
      if (responseCode != 0) {
        throw new IllegalArgumentException("first by must be 0 for errors was " + responseCode);
      }
      return readError(in);
    } catch (IOException e) {
      throw new X11ProtocolException("exception when reading error", e);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy