content: // Generated by dart-wayland-scanner
  // https://github.com/your-repo/dart-wayland-scanner
  // XML file : https://gitlab.freedesktop.org/wayland/wayland/-/raw/main/protocol/wayland.xml
  //
  // wayland Protocol Copyright:
  ///
  /// Copyright © 2008-2011 Kristian Høgsberg
  /// Copyright © 2010-2011 Intel Corporation
  /// Copyright © 2012-2013 Collabora, Ltd.
  ///
  /// Permission is hereby granted, free of charge, to any person
  /// obtaining a copy of this software and associated documentation files
  /// (the "Software"), to deal in the Software without restriction,
  /// including without limitation the rights to use, copy, modify, merge,
  /// publish, distribute, sublicense, and/or sell copies of the Software,
  /// and to permit persons to whom the Software is furnished to do so,
  /// subject to the following conditions:
  ///
  /// The above copyright notice and this permission notice (including the
  /// next paragraph) shall be included in all copies or substantial
  /// portions of the Software.
  ///
  /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  /// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  /// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  /// NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  /// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  /// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  /// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  /// SOFTWARE.
  ///
  
  library client;
  
  import 'package:wayland/wayland.dart';
  import 'dart:async';
  import 'dart:convert';
  import 'dart:typed_data';
  import 'package:result_dart/result_dart.dart';
  
  /// fatal error event
  ///
  /// The error event is sent out when a fatal (non-recoverable)
  /// error has occurred.  The object_id argument is the object
  /// where the error occurred, most often in response to a request
  /// to that object.  The code identifies the error and is defined
  /// by the object interface.  As such, each interface defines its
  /// own set of error codes.  The message is a brief description
  /// of the error, for (debugging) convenience.
  ///
  class DisplayErrorEvent {
    /// object where the error occurred
    final int objectId;
  
    /// error code
    final int code;
  
    /// error description
    final String message;
  
    DisplayErrorEvent(
      this.objectId,
      this.code,
      this.message,
    );
  
    @override
    toString() {
      return "DisplayErrorEvent (objectId: $objectId, code: $code, message: $message)";
    }
  }
  
  typedef DisplayErrorEventHandler = void Function(DisplayErrorEvent);
  
  /// acknowledge object ID deletion
  ///
  /// This event is used internally by the object ID management
  /// logic. When a client deletes an object that it had created,
  /// the server will send this event to acknowledge that it has
  /// seen the delete request. When the client receives this event,
  /// it will know that it can safely reuse the object ID.
  ///
  class DisplayDeleteIdEvent {
    /// deleted object ID
    final int id;
  
    DisplayDeleteIdEvent(
      this.id,
    );
  
    @override
    toString() {
      return "DisplayDeleteIdEvent (id: $id)";
    }
  }
  
  typedef DisplayDeleteIdEventHandler = void Function(DisplayDeleteIdEvent);
  
  /// core global object
  ///
  /// The core global object.  This is a special singleton object.  It
  /// is used for internal Wayland protocol features.
  ///
  class Display extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 1;
  
    Display(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Display {name: 'wl_display', id: '$objectId', version: '1',}";
    }
  
    /// asynchronous roundtrip
    ///
    /// The sync request asks the server to emit the 'done' event
    /// on the returned wl_callback object.  Since requests are
    /// handled in-order and events are delivered in-order, this can
    /// be used as a barrier to ensure all previous requests and the
    /// resulting events have been handled.
    ///
    /// The object returned by this request will be destroyed by the
    /// compositor after the callback is fired and as such the client must not
    /// attempt to use it after that point.
    ///
    /// The callback_data passed in the callback is undefined and should be ignored.
    ///
    /// [callback]: callback object for the sync request
    Result<Callback, Object> sync() {
      var callback = Callback(innerContext);
      logLn("Display::sync  callback: $callback");
      var arguments = [callback];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([callback.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Display::sync: $e");
        return Failure(e);
      }
      return Success(callback);
    }
  
    /// get global registry object
    ///
    /// This request creates a registry object that allows the client
    /// to list and bind the global objects available from the
    /// compositor.
    ///
    /// It should be noted that the server side resources consumed in
    /// response to a get_registry request can only be released when the
    /// client disconnects, not when the client side proxy is destroyed.
    /// Therefore, clients should invoke get_registry as infrequently as
    /// possible to avoid wasting memory.
    ///
    /// [registry]: global registry object
    Result<Registry, Object> getRegistry() {
      var registry = Registry(innerContext);
      logLn("Display::getRegistry  registry: $registry");
      var arguments = [registry];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([registry.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Display::getRegistry: $e");
        return Failure(e);
      }
      return Success(registry);
    }
  
    /// fatal error event
    ///
    /// The error event is sent out when a fatal (non-recoverable)
    /// error has occurred.  The object_id argument is the object
    /// where the error occurred, most often in response to a request
    /// to that object.  The code identifies the error and is defined
    /// by the object interface.  As such, each interface defines its
    /// own set of error codes.  The message is a brief description
    /// of the error, for (debugging) convenience.
    ///
    /// Event handler for Error
    /// - [object_id]: object where the error occurred
    /// - [code]: error code
    /// - [message]: error description
    void onError(DisplayErrorEventHandler handler) {
      _errorHandler = handler;
    }
  
    DisplayErrorEventHandler? _errorHandler;
  
    /// acknowledge object ID deletion
    ///
    /// This event is used internally by the object ID management
    /// logic. When a client deletes an object that it had created,
    /// the server will send this event to acknowledge that it has
    /// seen the delete request. When the client receives this event,
    /// it will know that it can safely reuse the object ID.
    ///
    /// Event handler for DeleteId
    /// - [id]: deleted object ID
    void onDeleteId(DisplayDeleteIdEventHandler handler) {
      _deleteIdHandler = handler;
    }
  
    DisplayDeleteIdEventHandler? _deleteIdHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Display.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_errorHandler != null) {
            var offset = 0;
            final objectId = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            final code =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final messageLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final message =
                utf8.decode(data.sublist(offset, offset + messageLength - 1));
            offset += messageLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            var event = DisplayErrorEvent(
              objectId,
              code,
              message,
            );
            _errorHandler!(event);
          }
          break;
        case 1:
          if (_deleteIdHandler != null) {
            var offset = 0;
            final id =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = DisplayDeleteIdEvent(
              id,
            );
            _deleteIdHandler!(event);
          }
          break;
      }
    }
  }
  
  /// global error values
  ///
  /// These errors are global and can be emitted in response to any
  /// server request.
  ///
  
  enum DisplayError {
    /// server couldn't find object
    invalidObject("invalid_object", 0),
  
    /// method doesn't exist on the specified interface or malformed request
    invalidMethod("invalid_method", 1),
  
    /// server is out of memory
    noMemory("no_memory", 2),
  
    /// implementation error in compositor
    implementation("implementation", 3);
  
    const DisplayError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "DisplayError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// announce global object
  ///
  /// Notify the client of global objects.
  ///
  /// The event notifies the client that a global object with
  /// the given name is now available, and it implements the
  /// given version of the given interface.
  ///
  class RegistryGlobalEvent {
    /// numeric name of the global object
    final int name;
  
    /// interface implemented by the object
    final String interface;
  
    /// interface version
    final int version;
  
    RegistryGlobalEvent(
      this.name,
      this.interface,
      this.version,
    );
  
    @override
    toString() {
      return "RegistryGlobalEvent (name: $name, interface: $interface, version: $version)";
    }
  }
  
  typedef RegistryGlobalEventHandler = void Function(RegistryGlobalEvent);
  
  /// announce removal of global object
  ///
  /// Notify the client of removed global objects.
  ///
  /// This event notifies the client that the global identified
  /// by name is no longer available.  If the client bound to
  /// the global using the bind request, the client should now
  /// destroy that object.
  ///
  /// The object remains valid and requests to the object will be
  /// ignored until the client destroys it, to avoid races between
  /// the global going away and a client sending a request to it.
  ///
  class RegistryGlobalRemoveEvent {
    /// numeric name of the global object
    final int name;
  
    RegistryGlobalRemoveEvent(
      this.name,
    );
  
    @override
    toString() {
      return "RegistryGlobalRemoveEvent (name: $name)";
    }
  }
  
  typedef RegistryGlobalRemoveEventHandler = void Function(
      RegistryGlobalRemoveEvent);
  
  /// global registry object
  ///
  /// The singleton global registry object.  The server has a number of
  /// global objects that are available to all clients.  These objects
  /// typically represent an actual object in the server (for example,
  /// an input device) or they are singleton objects that provide
  /// extension functionality.
  ///
  /// When a client creates a registry object, the registry object
  /// will emit a global event for each global currently in the
  /// registry.  Globals come and go as a result of device or
  /// monitor hotplugs, reconfiguration or other events, and the
  /// registry will send out global and global_remove events to
  /// keep the client up to date with the changes.  To mark the end
  /// of the initial burst of events, the client can use the
  /// wl_display.sync request immediately after calling
  /// wl_display.get_registry.
  ///
  /// A client can bind to a global object by using the bind
  /// request.  This creates a client-side handle that lets the object
  /// emit events to the client and lets the client invoke requests on
  /// the object.
  ///
  class Registry extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 1;
  
    Registry(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Registry {name: 'wl_registry', id: '$objectId', version: '1',}";
    }
  
    /// bind an object to the display
    ///
    /// Binds a new, client-created object to the server using the
    /// specified name as the identifier.
    ///
    /// [name]: unique numeric name of the object
    /// [id]: bounded object
    Result<void, Object> bind(int name, String interface, int version, int id) {
      logLn(
          "Registry::bind  name: $name interface: $interface version: $version id: $id");
      var arguments = [name, interface, version, id];
      var argTypes = <WaylandType>[
        WaylandType.uint,
        WaylandType.string,
        WaylandType.int,
        WaylandType.newId
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([name]).buffer.asUint8List());
      final interfaceBytes = utf8.encode(interface);
      bytesBuilder.add(
          Uint32List.fromList([interfaceBytes.length + 1]).buffer.asUint8List());
      bytesBuilder.add(interfaceBytes);
      bytesBuilder.add([0]); // Null terminator
      while (bytesBuilder.length % 4 != 0) {
        bytesBuilder.add([0]);
      } // Padding
      bytesBuilder.add(Int32List.fromList([version]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([id]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Registry::bind: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// announce global object
    ///
    /// Notify the client of global objects.
    ///
    /// The event notifies the client that a global object with
    /// the given name is now available, and it implements the
    /// given version of the given interface.
    ///
    /// Event handler for Global
    /// - [name]: numeric name of the global object
    /// - [interface]: interface implemented by the object
    /// - [version]: interface version
    void onGlobal(RegistryGlobalEventHandler handler) {
      _globalHandler = handler;
    }
  
    RegistryGlobalEventHandler? _globalHandler;
  
    /// announce removal of global object
    ///
    /// Notify the client of removed global objects.
    ///
    /// This event notifies the client that the global identified
    /// by name is no longer available.  If the client bound to
    /// the global using the bind request, the client should now
    /// destroy that object.
    ///
    /// The object remains valid and requests to the object will be
    /// ignored until the client destroys it, to avoid races between
    /// the global going away and a client sending a request to it.
    ///
    /// Event handler for GlobalRemove
    /// - [name]: numeric name of the global object
    void onGlobalRemove(RegistryGlobalRemoveEventHandler handler) {
      _globalRemoveHandler = handler;
    }
  
    RegistryGlobalRemoveEventHandler? _globalRemoveHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Registry.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_globalHandler != null) {
            var offset = 0;
            final name =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final interfaceLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final interface =
                utf8.decode(data.sublist(offset, offset + interfaceLength - 1));
            offset +=
                interfaceLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            final version =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = RegistryGlobalEvent(
              name,
              interface,
              version,
            );
            _globalHandler!(event);
          }
          break;
        case 1:
          if (_globalRemoveHandler != null) {
            var offset = 0;
            final name =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = RegistryGlobalRemoveEvent(
              name,
            );
            _globalRemoveHandler!(event);
          }
          break;
      }
    }
  }
  
  /// done event
  ///
  /// Notify the client when the related request is done.
  ///
  class CallbackDoneEvent {
    /// request-specific data for the callback
    final int callbackData;
  
    CallbackDoneEvent(
      this.callbackData,
    );
  
    @override
    toString() {
      return "CallbackDoneEvent (callbackData: $callbackData)";
    }
  }
  
  typedef CallbackDoneEventHandler = void Function(CallbackDoneEvent);
  
  /// callback object
  ///
  /// Clients can handle the 'done' event to get notified when
  /// the related request is done.
  ///
  /// Note, because wl_callback objects are created from multiple independent
  /// factory interfaces, the wl_callback interface is frozen at version 1.
  ///
  class Callback extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 1;
  
    Callback(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Callback {name: 'wl_callback', id: '$objectId', version: '1',}";
    }
  
    /// done event
    ///
    /// Notify the client when the related request is done.
    ///
    /// Event handler for Done
    /// - [callback_data]: request-specific data for the callback
    void onDone(CallbackDoneEventHandler handler) {
      _doneHandler = handler;
    }
  
    CallbackDoneEventHandler? _doneHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Callback.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_doneHandler != null) {
            var offset = 0;
            final callbackData =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = CallbackDoneEvent(
              callbackData,
            );
            _doneHandler!(event);
          }
          break;
      }
    }
  }
  
  /// the compositor singleton
  ///
  /// A compositor.  This object is a singleton global.  The
  /// compositor is in charge of combining the contents of multiple
  /// surfaces into one displayable output.
  ///
  class Compositor extends Proxy {
    final Context innerContext;
    final version = 6;
  
    Compositor(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Compositor {name: 'wl_compositor', id: '$objectId', version: '6',}";
    }
  
    /// create new surface
    ///
    /// Ask the compositor to create a new surface.
    ///
    /// [id]: the new surface
    Result<Surface, Object> createSurface() {
      var id = Surface(innerContext);
      logLn("Compositor::createSurface  id: $id");
      var arguments = [id];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Compositor::createSurface: $e");
        return Failure(e);
      }
      return Success(id);
    }
  
    /// create new region
    ///
    /// Ask the compositor to create a new region.
    ///
    /// [id]: the new region
    Result<Region, Object> createRegion() {
      var id = Region(innerContext);
      logLn("Compositor::createRegion  id: $id");
      var arguments = [id];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Compositor::createRegion: $e");
        return Failure(e);
      }
      return Success(id);
    }
  }
  
  /// a shared memory pool
  ///
  /// The wl_shm_pool object encapsulates a piece of memory shared
  /// between the compositor and client.  Through the wl_shm_pool
  /// object, the client can allocate shared memory wl_buffer objects.
  /// All objects created through the same pool share the same
  /// underlying mapped memory. Reusing the mapped memory avoids the
  /// setup/teardown overhead and is useful when interactively resizing
  /// a surface or for many small buffers.
  ///
  class ShmPool extends Proxy {
    final Context innerContext;
    final version = 2;
  
    ShmPool(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "ShmPool {name: 'wl_shm_pool', id: '$objectId', version: '2',}";
    }
  
    /// create a buffer from the pool
    ///
    /// Create a wl_buffer object from the pool.
    ///
    /// The buffer is created offset bytes into the pool and has
    /// width and height as specified.  The stride argument specifies
    /// the number of bytes from the beginning of one row to the beginning
    /// of the next.  The format is the pixel format of the buffer and
    /// must be one of those advertised through the wl_shm.format event.
    ///
    /// A buffer will keep a reference to the pool it was created from
    /// so it is valid to destroy the pool immediately after creating
    /// a buffer from it.
    ///
    /// [id]: buffer to create
    /// [offset]: buffer byte offset within the pool
    /// [width]: buffer width, in pixels
    /// [height]: buffer height, in pixels
    /// [stride]: number of bytes from the beginning of one row to the beginning of the next row
    /// [format]: buffer pixel format
    Result<Buffer, Object> createBuffer(
        int offset, int width, int height, int stride, int format) {
      var id = Buffer(innerContext);
      logLn(
          "ShmPool::createBuffer  id: $id offset: $offset width: $width height: $height stride: $stride format: $format");
      var arguments = [id, offset, width, height, stride, format];
      var argTypes = <WaylandType>[
        WaylandType.newId,
        WaylandType.int,
        WaylandType.int,
        WaylandType.int,
        WaylandType.int,
        WaylandType.uint
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([offset]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([width]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([height]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([stride]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([format]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShmPool::createBuffer: $e");
        return Failure(e);
      }
      return Success(id);
    }
  
    /// destroy the pool
    ///
    /// Destroy the shared memory pool.
    ///
    /// The mmapped memory will be released when all
    /// buffers that have been created from this pool
    /// are gone.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("ShmPool::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShmPool::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// change the size of the pool mapping
    ///
    /// This request will cause the server to remap the backing memory
    /// for the pool from the file descriptor passed when the pool was
    /// created, but using the new size.  This request can only be
    /// used to make the pool bigger.
    ///
    /// This request only changes the amount of bytes that are mmapped
    /// by the server and does not touch the file corresponding to the
    /// file descriptor passed at creation time. It is the client's
    /// responsibility to ensure that the file is at least as big as
    /// the new pool size.
    ///
    /// [size]: new size of the pool, in bytes
    Result<void, Object> resize(int size) {
      logLn("ShmPool::resize  size: $size");
      var arguments = [size];
      var argTypes = <WaylandType>[WaylandType.int];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([size]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShmPool::resize: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  }
  
  /// pixel format description
  ///
  /// Informs the client about a valid pixel format that
  /// can be used for buffers. Known formats include
  /// argb8888 and xrgb8888.
  ///
  class ShmFormatEvent {
    /// buffer pixel format
    final int format;
  
    ShmFormatEvent(
      this.format,
    );
  
    @override
    toString() {
      return "ShmFormatEvent (format: $format)";
    }
  }
  
  typedef ShmFormatEventHandler = void Function(ShmFormatEvent);
  
  /// shared memory support
  ///
  /// A singleton global object that provides support for shared
  /// memory.
  ///
  /// Clients can create wl_shm_pool objects using the create_pool
  /// request.
  ///
  /// On binding the wl_shm object one or more format events
  /// are emitted to inform clients about the valid pixel formats
  /// that can be used for buffers.
  ///
  class Shm extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 2;
  
    Shm(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Shm {name: 'wl_shm', id: '$objectId', version: '2',}";
    }
  
    /// create a shm pool
    ///
    /// Create a new wl_shm_pool object.
    ///
    /// The pool can be used to create shared memory based buffer
    /// objects.  The server will mmap size bytes of the passed file
    /// descriptor, to use as backing memory for the pool.
    ///
    /// [id]: pool to create
    /// [fd]: file descriptor for the pool
    /// [size]: pool size, in bytes
    Result<ShmPool, Object> createPool(int fd, int size) {
      var id = ShmPool(innerContext);
      logLn("Shm::createPool  id: $id fd: $fd size: $size");
      var arguments = [id, size];
      var argTypes = <WaylandType>[WaylandType.newId, WaylandType.int];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([size]).buffer.asUint8List());
      try {
        innerContext.sendMessage(bytesBuilder.toBytes(), fd);
      } catch (e) {
        logLn("Exception in Shm::createPool: $e");
        return Failure(e);
      }
      return Success(id);
    }
  
    /// release the shm object
    ///
    /// Using this request a client can tell the server that it is not going to
    /// use the shm object anymore.
    ///
    /// Objects created via this interface remain unaffected.
    ///
    Result<void, Object> release() {
      innerContext.unRegister(this);
      logLn("Shm::release ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Shm::release: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// pixel format description
    ///
    /// Informs the client about a valid pixel format that
    /// can be used for buffers. Known formats include
    /// argb8888 and xrgb8888.
    ///
    /// Event handler for Format
    /// - [format]: buffer pixel format
    void onFormat(ShmFormatEventHandler handler) {
      _formatHandler = handler;
    }
  
    ShmFormatEventHandler? _formatHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Shm.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_formatHandler != null) {
            var offset = 0;
            final format =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = ShmFormatEvent(
              format,
            );
            _formatHandler!(event);
          }
          break;
      }
    }
  }
  
  /// wl_shm error values
  ///
  /// These errors can be emitted in response to wl_shm requests.
  ///
  
  enum ShmError {
    /// buffer format is not known
    invalidFormat("invalid_format", 0),
  
    /// invalid size or stride during pool or buffer creation
    invalidStride("invalid_stride", 1),
  
    /// mmapping the file descriptor failed
    invalidFd("invalid_fd", 2);
  
    const ShmError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "ShmError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// pixel formats
  ///
  /// This describes the memory layout of an individual pixel.
  ///
  /// All renderers should support argb8888 and xrgb8888 but any other
  /// formats are optional and may not be supported by the particular
  /// renderer in use.
  ///
  /// The drm format codes match the macros defined in drm_fourcc.h, except
  /// argb8888 and xrgb8888. The formats actually supported by the compositor
  /// will be reported by the format event.
  ///
  /// For all wl_shm formats and unless specified in another protocol
  /// extension, pre-multiplied alpha is used for pixel values.
  ///
  
  enum ShmFormat {
    /// 32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian
    argb8888("argb8888", 0),
  
    /// 32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian
    xrgb8888("xrgb8888", 1),
  
    /// 8-bit color index format, [7:0] C
    c8("c8", 0x20203843),
  
    /// 8-bit RGB format, [7:0] R:G:B 3:3:2
    rgb332("rgb332", 0x38424752),
  
    /// 8-bit BGR format, [7:0] B:G:R 2:3:3
    bgr233("bgr233", 0x38524742),
  
    /// 16-bit xRGB format, [15:0] x:R:G:B 4:4:4:4 little endian
    xrgb4444("xrgb4444", 0x32315258),
  
    /// 16-bit xBGR format, [15:0] x:B:G:R 4:4:4:4 little endian
    xbgr4444("xbgr4444", 0x32314258),
  
    /// 16-bit RGBx format, [15:0] R:G:B:x 4:4:4:4 little endian
    rgbx4444("rgbx4444", 0x32315852),
  
    /// 16-bit BGRx format, [15:0] B:G:R:x 4:4:4:4 little endian
    bgrx4444("bgrx4444", 0x32315842),
  
    /// 16-bit ARGB format, [15:0] A:R:G:B 4:4:4:4 little endian
    argb4444("argb4444", 0x32315241),
  
    /// 16-bit ABGR format, [15:0] A:B:G:R 4:4:4:4 little endian
    abgr4444("abgr4444", 0x32314241),
  
    /// 16-bit RBGA format, [15:0] R:G:B:A 4:4:4:4 little endian
    rgba4444("rgba4444", 0x32314152),
  
    /// 16-bit BGRA format, [15:0] B:G:R:A 4:4:4:4 little endian
    bgra4444("bgra4444", 0x32314142),
  
    /// 16-bit xRGB format, [15:0] x:R:G:B 1:5:5:5 little endian
    xrgb1555("xrgb1555", 0x35315258),
  
    /// 16-bit xBGR 1555 format, [15:0] x:B:G:R 1:5:5:5 little endian
    xbgr1555("xbgr1555", 0x35314258),
  
    /// 16-bit RGBx 5551 format, [15:0] R:G:B:x 5:5:5:1 little endian
    rgbx5551("rgbx5551", 0x35315852),
  
    /// 16-bit BGRx 5551 format, [15:0] B:G:R:x 5:5:5:1 little endian
    bgrx5551("bgrx5551", 0x35315842),
  
    /// 16-bit ARGB 1555 format, [15:0] A:R:G:B 1:5:5:5 little endian
    argb1555("argb1555", 0x35315241),
  
    /// 16-bit ABGR 1555 format, [15:0] A:B:G:R 1:5:5:5 little endian
    abgr1555("abgr1555", 0x35314241),
  
    /// 16-bit RGBA 5551 format, [15:0] R:G:B:A 5:5:5:1 little endian
    rgba5551("rgba5551", 0x35314152),
  
    /// 16-bit BGRA 5551 format, [15:0] B:G:R:A 5:5:5:1 little endian
    bgra5551("bgra5551", 0x35314142),
  
    /// 16-bit RGB 565 format, [15:0] R:G:B 5:6:5 little endian
    rgb565("rgb565", 0x36314752),
  
    /// 16-bit BGR 565 format, [15:0] B:G:R 5:6:5 little endian
    bgr565("bgr565", 0x36314742),
  
    /// 24-bit RGB format, [23:0] R:G:B little endian
    rgb888("rgb888", 0x34324752),
  
    /// 24-bit BGR format, [23:0] B:G:R little endian
    bgr888("bgr888", 0x34324742),
  
    /// 32-bit xBGR format, [31:0] x:B:G:R 8:8:8:8 little endian
    xbgr8888("xbgr8888", 0x34324258),
  
    /// 32-bit RGBx format, [31:0] R:G:B:x 8:8:8:8 little endian
    rgbx8888("rgbx8888", 0x34325852),
  
    /// 32-bit BGRx format, [31:0] B:G:R:x 8:8:8:8 little endian
    bgrx8888("bgrx8888", 0x34325842),
  
    /// 32-bit ABGR format, [31:0] A:B:G:R 8:8:8:8 little endian
    abgr8888("abgr8888", 0x34324241),
  
    /// 32-bit RGBA format, [31:0] R:G:B:A 8:8:8:8 little endian
    rgba8888("rgba8888", 0x34324152),
  
    /// 32-bit BGRA format, [31:0] B:G:R:A 8:8:8:8 little endian
    bgra8888("bgra8888", 0x34324142),
  
    /// 32-bit xRGB format, [31:0] x:R:G:B 2:10:10:10 little endian
    xrgb2101010("xrgb2101010", 0x30335258),
  
    /// 32-bit xBGR format, [31:0] x:B:G:R 2:10:10:10 little endian
    xbgr2101010("xbgr2101010", 0x30334258),
  
    /// 32-bit RGBx format, [31:0] R:G:B:x 10:10:10:2 little endian
    rgbx1010102("rgbx1010102", 0x30335852),
  
    /// 32-bit BGRx format, [31:0] B:G:R:x 10:10:10:2 little endian
    bgrx1010102("bgrx1010102", 0x30335842),
  
    /// 32-bit ARGB format, [31:0] A:R:G:B 2:10:10:10 little endian
    argb2101010("argb2101010", 0x30335241),
  
    /// 32-bit ABGR format, [31:0] A:B:G:R 2:10:10:10 little endian
    abgr2101010("abgr2101010", 0x30334241),
  
    /// 32-bit RGBA format, [31:0] R:G:B:A 10:10:10:2 little endian
    rgba1010102("rgba1010102", 0x30334152),
  
    /// 32-bit BGRA format, [31:0] B:G:R:A 10:10:10:2 little endian
    bgra1010102("bgra1010102", 0x30334142),
  
    /// packed YCbCr format, [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian
    yuyv("yuyv", 0x56595559),
  
    /// packed YCbCr format, [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian
    yvyu("yvyu", 0x55595659),
  
    /// packed YCbCr format, [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian
    uyvy("uyvy", 0x59565955),
  
    /// packed YCbCr format, [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian
    vyuy("vyuy", 0x59555956),
  
    /// packed AYCbCr format, [31:0] A:Y:Cb:Cr 8:8:8:8 little endian
    ayuv("ayuv", 0x56555941),
  
    /// 2 plane YCbCr Cr:Cb format, 2x2 subsampled Cr:Cb plane
    nv12("nv12", 0x3231564e),
  
    /// 2 plane YCbCr Cb:Cr format, 2x2 subsampled Cb:Cr plane
    nv21("nv21", 0x3132564e),
  
    /// 2 plane YCbCr Cr:Cb format, 2x1 subsampled Cr:Cb plane
    nv16("nv16", 0x3631564e),
  
    /// 2 plane YCbCr Cb:Cr format, 2x1 subsampled Cb:Cr plane
    nv61("nv61", 0x3136564e),
  
    /// 3 plane YCbCr format, 4x4 subsampled Cb (1) and Cr (2) planes
    yuv410("yuv410", 0x39565559),
  
    /// 3 plane YCbCr format, 4x4 subsampled Cr (1) and Cb (2) planes
    yvu410("yvu410", 0x39555659),
  
    /// 3 plane YCbCr format, 4x1 subsampled Cb (1) and Cr (2) planes
    yuv411("yuv411", 0x31315559),
  
    /// 3 plane YCbCr format, 4x1 subsampled Cr (1) and Cb (2) planes
    yvu411("yvu411", 0x31315659),
  
    /// 3 plane YCbCr format, 2x2 subsampled Cb (1) and Cr (2) planes
    yuv420("yuv420", 0x32315559),
  
    /// 3 plane YCbCr format, 2x2 subsampled Cr (1) and Cb (2) planes
    yvu420("yvu420", 0x32315659),
  
    /// 3 plane YCbCr format, 2x1 subsampled Cb (1) and Cr (2) planes
    yuv422("yuv422", 0x36315559),
  
    /// 3 plane YCbCr format, 2x1 subsampled Cr (1) and Cb (2) planes
    yvu422("yvu422", 0x36315659),
  
    /// 3 plane YCbCr format, non-subsampled Cb (1) and Cr (2) planes
    yuv444("yuv444", 0x34325559),
  
    /// 3 plane YCbCr format, non-subsampled Cr (1) and Cb (2) planes
    yvu444("yvu444", 0x34325659),
  
    /// [7:0] R
    r8("r8", 0x20203852),
  
    /// [15:0] R little endian
    r16("r16", 0x20363152),
  
    /// [15:0] R:G 8:8 little endian
    rg88("rg88", 0x38384752),
  
    /// [15:0] G:R 8:8 little endian
    gr88("gr88", 0x38385247),
  
    /// [31:0] R:G 16:16 little endian
    rg1616("rg1616", 0x32334752),
  
    /// [31:0] G:R 16:16 little endian
    gr1616("gr1616", 0x32335247),
  
    /// [63:0] x:R:G:B 16:16:16:16 little endian
    xrgb16161616f("xrgb16161616f", 0x48345258),
  
    /// [63:0] x:B:G:R 16:16:16:16 little endian
    xbgr16161616f("xbgr16161616f", 0x48344258),
  
    /// [63:0] A:R:G:B 16:16:16:16 little endian
    argb16161616f("argb16161616f", 0x48345241),
  
    /// [63:0] A:B:G:R 16:16:16:16 little endian
    abgr16161616f("abgr16161616f", 0x48344241),
  
    /// [31:0] X:Y:Cb:Cr 8:8:8:8 little endian
    xyuv8888("xyuv8888", 0x56555958),
  
    /// [23:0] Cr:Cb:Y 8:8:8 little endian
    vuy888("vuy888", 0x34325556),
  
    /// Y followed by U then V, 10:10:10. Non-linear modifier only
    vuy101010("vuy101010", 0x30335556),
  
    /// [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels
    y210("y210", 0x30313259),
  
    /// [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels
    y212("y212", 0x32313259),
  
    /// [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels
    y216("y216", 0x36313259),
  
    /// [31:0] A:Cr:Y:Cb 2:10:10:10 little endian
    y410("y410", 0x30313459),
  
    /// [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian
    y412("y412", 0x32313459),
  
    /// [63:0] A:Cr:Y:Cb 16:16:16:16 little endian
    y416("y416", 0x36313459),
  
    /// [31:0] X:Cr:Y:Cb 2:10:10:10 little endian
    xvyu2101010("xvyu2101010", 0x30335658),
  
    /// [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian
    xvyu1216161616("xvyu12_16161616", 0x36335658),
  
    /// [63:0] X:Cr:Y:Cb 16:16:16:16 little endian
    xvyu16161616("xvyu16161616", 0x38345658),
  
    /// [63:0]   A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0  1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian
    y0l0("y0l0", 0x304c3059),
  
    /// [63:0]   X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0  1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian
    x0l0("x0l0", 0x304c3058),
  
    /// [63:0]   A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0  1:1:10:10:10:1:1:10:10:10 little endian
    y0l2("y0l2", 0x324c3059),
  
    /// [63:0]   X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0  1:1:10:10:10:1:1:10:10:10 little endian
    x0l2("x0l2", 0x324c3058),
  
    ///
    yuv4208bit("yuv420_8bit", 0x38305559),
  
    ///
    yuv42010bit("yuv420_10bit", 0x30315559),
  
    ///
    xrgb8888A8("xrgb8888_a8", 0x38415258),
  
    ///
    xbgr8888A8("xbgr8888_a8", 0x38414258),
  
    ///
    rgbx8888A8("rgbx8888_a8", 0x38415852),
  
    ///
    bgrx8888A8("bgrx8888_a8", 0x38415842),
  
    ///
    rgb888A8("rgb888_a8", 0x38413852),
  
    ///
    bgr888A8("bgr888_a8", 0x38413842),
  
    ///
    rgb565A8("rgb565_a8", 0x38413552),
  
    ///
    bgr565A8("bgr565_a8", 0x38413542),
  
    /// non-subsampled Cr:Cb plane
    nv24("nv24", 0x3432564e),
  
    /// non-subsampled Cb:Cr plane
    nv42("nv42", 0x3234564e),
  
    /// 2x1 subsampled Cr:Cb plane, 10 bit per channel
    p210("p210", 0x30313250),
  
    /// 2x2 subsampled Cr:Cb plane 10 bits per channel
    p010("p010", 0x30313050),
  
    /// 2x2 subsampled Cr:Cb plane 12 bits per channel
    p012("p012", 0x32313050),
  
    /// 2x2 subsampled Cr:Cb plane 16 bits per channel
    p016("p016", 0x36313050),
  
    /// [63:0] A:x:B:x:G:x:R:x 10:6:10:6:10:6:10:6 little endian
    axbxgxrx106106106106("axbxgxrx106106106106", 0x30314241),
  
    /// 2x2 subsampled Cr:Cb plane
    nv15("nv15", 0x3531564e),
  
    ///
    q410("q410", 0x30313451),
  
    ///
    q401("q401", 0x31303451),
  
    /// [63:0] x:R:G:B 16:16:16:16 little endian
    xrgb16161616("xrgb16161616", 0x38345258),
  
    /// [63:0] x:B:G:R 16:16:16:16 little endian
    xbgr16161616("xbgr16161616", 0x38344258),
  
    /// [63:0] A:R:G:B 16:16:16:16 little endian
    argb16161616("argb16161616", 0x38345241),
  
    /// [63:0] A:B:G:R 16:16:16:16 little endian
    abgr16161616("abgr16161616", 0x38344241),
  
    /// [7:0] C0:C1:C2:C3:C4:C5:C6:C7 1:1:1:1:1:1:1:1 eight pixels/byte
    c1("c1", 0x20203143),
  
    /// [7:0] C0:C1:C2:C3 2:2:2:2 four pixels/byte
    c2("c2", 0x20203243),
  
    /// [7:0] C0:C1 4:4 two pixels/byte
    c4("c4", 0x20203443),
  
    /// [7:0] D0:D1:D2:D3:D4:D5:D6:D7 1:1:1:1:1:1:1:1 eight pixels/byte
    d1("d1", 0x20203144),
  
    /// [7:0] D0:D1:D2:D3 2:2:2:2 four pixels/byte
    d2("d2", 0x20203244),
  
    /// [7:0] D0:D1 4:4 two pixels/byte
    d4("d4", 0x20203444),
  
    /// [7:0] D
    d8("d8", 0x20203844),
  
    /// [7:0] R0:R1:R2:R3:R4:R5:R6:R7 1:1:1:1:1:1:1:1 eight pixels/byte
    r1("r1", 0x20203152),
  
    /// [7:0] R0:R1:R2:R3 2:2:2:2 four pixels/byte
    r2("r2", 0x20203252),
  
    /// [7:0] R0:R1 4:4 two pixels/byte
    r4("r4", 0x20203452),
  
    /// [15:0] x:R 6:10 little endian
    r10("r10", 0x20303152),
  
    /// [15:0] x:R 4:12 little endian
    r12("r12", 0x20323152),
  
    /// [31:0] A:Cr:Cb:Y 8:8:8:8 little endian
    avuy8888("avuy8888", 0x59555641),
  
    /// [31:0] X:Cr:Cb:Y 8:8:8:8 little endian
    xvuy8888("xvuy8888", 0x59555658),
  
    /// 2x2 subsampled Cr:Cb plane 10 bits per channel packed
    p030("p030", 0x30333050);
  
    const ShmFormat(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "ShmFormat {name: $enumName, value: $enumValue}";
    }
  }
  
  /// compositor releases buffer
  ///
  /// Sent when this wl_buffer is no longer used by the compositor.
  /// The client is now free to reuse or destroy this buffer and its
  /// backing storage.
  ///
  /// If a client receives a release event before the frame callback
  /// requested in the same wl_surface.commit that attaches this
  /// wl_buffer to a surface, then the client is immediately free to
  /// reuse the buffer and its backing storage, and does not need a
  /// second buffer for the next surface content update. Typically
  /// this is possible, when the compositor maintains a copy of the
  /// wl_surface contents, e.g. as a GL texture. This is an important
  /// optimization for GL(ES) compositors with wl_shm clients.
  ///
  class BufferReleaseEvent {
    BufferReleaseEvent();
  
    @override
    toString() {
      return "BufferReleaseEvent ()";
    }
  }
  
  typedef BufferReleaseEventHandler = void Function(BufferReleaseEvent);
  
  /// content for a wl_surface
  ///
  /// A buffer provides the content for a wl_surface. Buffers are
  /// created through factory interfaces such as wl_shm, wp_linux_buffer_params
  /// (from the linux-dmabuf protocol extension) or similar. It has a width and
  /// a height and can be attached to a wl_surface, but the mechanism by which a
  /// client provides and updates the contents is defined by the buffer factory
  /// interface.
  ///
  /// Color channels are assumed to be electrical rather than optical (in other
  /// words, encoded with a transfer function) unless otherwise specified. If
  /// the buffer uses a format that has an alpha channel, the alpha channel is
  /// assumed to be premultiplied into the electrical color channel values
  /// (after transfer function encoding) unless otherwise specified.
  ///
  /// Note, because wl_buffer objects are created from multiple independent
  /// factory interfaces, the wl_buffer interface is frozen at version 1.
  ///
  class Buffer extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 1;
  
    Buffer(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Buffer {name: 'wl_buffer', id: '$objectId', version: '1',}";
    }
  
    /// destroy a buffer
    ///
    /// Destroy a buffer. If and how you need to release the backing
    /// storage is defined by the buffer factory interface.
    ///
    /// For possible side-effects to a surface, see wl_surface.attach.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("Buffer::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Buffer::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// compositor releases buffer
    ///
    /// Sent when this wl_buffer is no longer used by the compositor.
    /// The client is now free to reuse or destroy this buffer and its
    /// backing storage.
    ///
    /// If a client receives a release event before the frame callback
    /// requested in the same wl_surface.commit that attaches this
    /// wl_buffer to a surface, then the client is immediately free to
    /// reuse the buffer and its backing storage, and does not need a
    /// second buffer for the next surface content update. Typically
    /// this is possible, when the compositor maintains a copy of the
    /// wl_surface contents, e.g. as a GL texture. This is an important
    /// optimization for GL(ES) compositors with wl_shm clients.
    ///
    /// Event handler for Release
    void onRelease(BufferReleaseEventHandler handler) {
      _releaseHandler = handler;
    }
  
    BufferReleaseEventHandler? _releaseHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Buffer.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_releaseHandler != null) {
            _releaseHandler!(BufferReleaseEvent());
          }
          break;
      }
    }
  }
  
  /// advertise offered mime type
  ///
  /// Sent immediately after creating the wl_data_offer object.  One
  /// event per offered mime type.
  ///
  class DataOfferOfferEvent {
    /// offered mime type
    final String mimeType;
  
    DataOfferOfferEvent(
      this.mimeType,
    );
  
    @override
    toString() {
      return "DataOfferOfferEvent (mimeType: $mimeType)";
    }
  }
  
  typedef DataOfferOfferEventHandler = void Function(DataOfferOfferEvent);
  
  /// notify the source-side available actions
  ///
  /// This event indicates the actions offered by the data source. It
  /// will be sent immediately after creating the wl_data_offer object,
  /// or anytime the source side changes its offered actions through
  /// wl_data_source.set_actions.
  ///
  class DataOfferSourceActionsEvent {
    /// actions offered by the data source
    final int sourceActions;
  
    DataOfferSourceActionsEvent(
      this.sourceActions,
    );
  
    @override
    toString() {
      return "DataOfferSourceActionsEvent (sourceActions: $sourceActions)";
    }
  }
  
  typedef DataOfferSourceActionsEventHandler = void Function(
      DataOfferSourceActionsEvent);
  
  /// notify the selected action
  ///
  /// This event indicates the action selected by the compositor after
  /// matching the source/destination side actions. Only one action (or
  /// none) will be offered here.
  ///
  /// This event can be emitted multiple times during the drag-and-drop
  /// operation in response to destination side action changes through
  /// wl_data_offer.set_actions.
  ///
  /// This event will no longer be emitted after wl_data_device.drop
  /// happened on the drag-and-drop destination, the client must
  /// honor the last action received, or the last preferred one set
  /// through wl_data_offer.set_actions when handling an "ask" action.
  ///
  /// Compositors may also change the selected action on the fly, mainly
  /// in response to keyboard modifier changes during the drag-and-drop
  /// operation.
  ///
  /// The most recent action received is always the valid one. Prior to
  /// receiving wl_data_device.drop, the chosen action may change (e.g.
  /// due to keyboard modifiers being pressed). At the time of receiving
  /// wl_data_device.drop the drag-and-drop destination must honor the
  /// last action received.
  ///
  /// Action changes may still happen after wl_data_device.drop,
  /// especially on "ask" actions, where the drag-and-drop destination
  /// may choose another action afterwards. Action changes happening
  /// at this stage are always the result of inter-client negotiation, the
  /// compositor shall no longer be able to induce a different action.
  ///
  /// Upon "ask" actions, it is expected that the drag-and-drop destination
  /// may potentially choose a different action and/or mime type,
  /// based on wl_data_offer.source_actions and finally chosen by the
  /// user (e.g. popping up a menu with the available options). The
  /// final wl_data_offer.set_actions and wl_data_offer.accept requests
  /// must happen before the call to wl_data_offer.finish.
  ///
  class DataOfferActionEvent {
    /// action selected by the compositor
    final int dndAction;
  
    DataOfferActionEvent(
      this.dndAction,
    );
  
    @override
    toString() {
      return "DataOfferActionEvent (dndAction: $dndAction)";
    }
  }
  
  typedef DataOfferActionEventHandler = void Function(DataOfferActionEvent);
  
  /// offer to transfer data
  ///
  /// A wl_data_offer represents a piece of data offered for transfer
  /// by another client (the source client).  It is used by the
  /// copy-and-paste and drag-and-drop mechanisms.  The offer
  /// describes the different mime types that the data can be
  /// converted to and provides the mechanism for transferring the
  /// data directly from the source client.
  ///
  class DataOffer extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 3;
  
    DataOffer(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "DataOffer {name: 'wl_data_offer', id: '$objectId', version: '3',}";
    }
  
    /// accept one of the offered mime types
    ///
    /// Indicate that the client can accept the given mime type, or
    /// NULL for not accepted.
    ///
    /// For objects of version 2 or older, this request is used by the
    /// client to give feedback whether the client can receive the given
    /// mime type, or NULL if none is accepted; the feedback does not
    /// determine whether the drag-and-drop operation succeeds or not.
    ///
    /// For objects of version 3 or newer, this request determines the
    /// final result of the drag-and-drop operation. If the end result
    /// is that no mime types were accepted, the drag-and-drop operation
    /// will be cancelled and the corresponding drag source will receive
    /// wl_data_source.cancelled. Clients may still use this event in
    /// conjunction with wl_data_source.action for feedback.
    ///
    /// [serial]: serial number of the accept request
    /// [mime_type]: mime type accepted by the client
    Result<void, Object> accept(int serial, String mimeType) {
      logLn("DataOffer::accept  serial: $serial mimeType: $mimeType");
      var arguments = [serial, mimeType];
      var argTypes = <WaylandType>[WaylandType.uint, WaylandType.string];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      final mimeTypeBytes = utf8.encode(mimeType);
      bytesBuilder.add(
          Uint32List.fromList([mimeTypeBytes.length + 1]).buffer.asUint8List());
      bytesBuilder.add(mimeTypeBytes);
      bytesBuilder.add([0]); // Null terminator
      while (bytesBuilder.length % 4 != 0) {
        bytesBuilder.add([0]);
      } // Padding
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataOffer::accept: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// request that the data is transferred
    ///
    /// To transfer the offered data, the client issues this request
    /// and indicates the mime type it wants to receive.  The transfer
    /// happens through the passed file descriptor (typically created
    /// with the pipe system call).  The source client writes the data
    /// in the mime type representation requested and then closes the
    /// file descriptor.
    ///
    /// The receiving client reads from the read end of the pipe until
    /// EOF and then closes its end, at which point the transfer is
    /// complete.
    ///
    /// This request may happen multiple times for different mime types,
    /// both before and after wl_data_device.drop. Drag-and-drop destination
    /// clients may preemptively fetch data or examine it more closely to
    /// determine acceptance.
    ///
    /// [mime_type]: mime type desired by receiver
    /// [fd]: file descriptor for data transfer
    Result<void, Object> receive(String mimeType, int fd) {
      logLn("DataOffer::receive  mimeType: $mimeType fd: $fd");
      var arguments = [mimeType];
      var argTypes = <WaylandType>[WaylandType.string];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      final mimeTypeBytes = utf8.encode(mimeType);
      bytesBuilder.add(
          Uint32List.fromList([mimeTypeBytes.length + 1]).buffer.asUint8List());
      bytesBuilder.add(mimeTypeBytes);
      bytesBuilder.add([0]); // Null terminator
      while (bytesBuilder.length % 4 != 0) {
        bytesBuilder.add([0]);
      } // Padding
      try {
        innerContext.sendMessage(bytesBuilder.toBytes(), fd);
      } catch (e) {
        logLn("Exception in DataOffer::receive: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// destroy data offer
    ///
    /// Destroy the data offer.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("DataOffer::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataOffer::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// the offer will no longer be used
    ///
    /// Notifies the compositor that the drag destination successfully
    /// finished the drag-and-drop operation.
    ///
    /// Upon receiving this request, the compositor will emit
    /// wl_data_source.dnd_finished on the drag source client.
    ///
    /// It is a client error to perform other requests than
    /// wl_data_offer.destroy after this one. It is also an error to perform
    /// this request after a NULL mime type has been set in
    /// wl_data_offer.accept or no action was received through
    /// wl_data_offer.action.
    ///
    /// If wl_data_offer.finish request is received for a non drag and drop
    /// operation, the invalid_finish protocol error is raised.
    ///
    Result<void, Object> finish() {
      logLn("DataOffer::finish ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 3])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataOffer::finish: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set the available/preferred drag-and-drop actions
    ///
    /// Sets the actions that the destination side client supports for
    /// this operation. This request may trigger the emission of
    /// wl_data_source.action and wl_data_offer.action events if the compositor
    /// needs to change the selected action.
    ///
    /// This request can be called multiple times throughout the
    /// drag-and-drop operation, typically in response to wl_data_device.enter
    /// or wl_data_device.motion events.
    ///
    /// This request determines the final result of the drag-and-drop
    /// operation. If the end result is that no action is accepted,
    /// the drag source will receive wl_data_source.cancelled.
    ///
    /// The dnd_actions argument must contain only values expressed in the
    /// wl_data_device_manager.dnd_actions enum, and the preferred_action
    /// argument must only contain one of those values set, otherwise it
    /// will result in a protocol error.
    ///
    /// While managing an "ask" action, the destination drag-and-drop client
    /// may perform further wl_data_offer.receive requests, and is expected
    /// to perform one last wl_data_offer.set_actions request with a preferred
    /// action other than "ask" (and optionally wl_data_offer.accept) before
    /// requesting wl_data_offer.finish, in order to convey the action selected
    /// by the user. If the preferred action is not in the
    /// wl_data_offer.source_actions mask, an error will be raised.
    ///
    /// If the "ask" action is dismissed (e.g. user cancellation), the client
    /// is expected to perform wl_data_offer.destroy right away.
    ///
    /// This request can only be made on drag-and-drop offers, a protocol error
    /// will be raised otherwise.
    ///
    /// [dnd_actions]: actions supported by the destination client
    /// [preferred_action]: action preferred by the destination client
    Result<void, Object> setActions(int dndActions, int preferredAction) {
      logLn(
          "DataOffer::setActions  dndActions: $dndActions preferredAction: $preferredAction");
      var arguments = [dndActions, preferredAction];
      var argTypes = <WaylandType>[WaylandType.uint, WaylandType.uint];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 4])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([dndActions]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([preferredAction]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataOffer::setActions: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// advertise offered mime type
    ///
    /// Sent immediately after creating the wl_data_offer object.  One
    /// event per offered mime type.
    ///
    /// Event handler for Offer
    /// - [mime_type]: offered mime type
    void onOffer(DataOfferOfferEventHandler handler) {
      _offerHandler = handler;
    }
  
    DataOfferOfferEventHandler? _offerHandler;
  
    /// notify the source-side available actions
    ///
    /// This event indicates the actions offered by the data source. It
    /// will be sent immediately after creating the wl_data_offer object,
    /// or anytime the source side changes its offered actions through
    /// wl_data_source.set_actions.
    ///
    /// Event handler for SourceActions
    /// - [source_actions]: actions offered by the data source
    void onSourceActions(DataOfferSourceActionsEventHandler handler) {
      _sourceActionsHandler = handler;
    }
  
    DataOfferSourceActionsEventHandler? _sourceActionsHandler;
  
    /// notify the selected action
    ///
    /// This event indicates the action selected by the compositor after
    /// matching the source/destination side actions. Only one action (or
    /// none) will be offered here.
    ///
    /// This event can be emitted multiple times during the drag-and-drop
    /// operation in response to destination side action changes through
    /// wl_data_offer.set_actions.
    ///
    /// This event will no longer be emitted after wl_data_device.drop
    /// happened on the drag-and-drop destination, the client must
    /// honor the last action received, or the last preferred one set
    /// through wl_data_offer.set_actions when handling an "ask" action.
    ///
    /// Compositors may also change the selected action on the fly, mainly
    /// in response to keyboard modifier changes during the drag-and-drop
    /// operation.
    ///
    /// The most recent action received is always the valid one. Prior to
    /// receiving wl_data_device.drop, the chosen action may change (e.g.
    /// due to keyboard modifiers being pressed). At the time of receiving
    /// wl_data_device.drop the drag-and-drop destination must honor the
    /// last action received.
    ///
    /// Action changes may still happen after wl_data_device.drop,
    /// especially on "ask" actions, where the drag-and-drop destination
    /// may choose another action afterwards. Action changes happening
    /// at this stage are always the result of inter-client negotiation, the
    /// compositor shall no longer be able to induce a different action.
    ///
    /// Upon "ask" actions, it is expected that the drag-and-drop destination
    /// may potentially choose a different action and/or mime type,
    /// based on wl_data_offer.source_actions and finally chosen by the
    /// user (e.g. popping up a menu with the available options). The
    /// final wl_data_offer.set_actions and wl_data_offer.accept requests
    /// must happen before the call to wl_data_offer.finish.
    ///
    /// Event handler for Action
    /// - [dnd_action]: action selected by the compositor
    void onAction(DataOfferActionEventHandler handler) {
      _actionHandler = handler;
    }
  
    DataOfferActionEventHandler? _actionHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("DataOffer.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_offerHandler != null) {
            var offset = 0;
            final mimeTypeLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final mimeType =
                utf8.decode(data.sublist(offset, offset + mimeTypeLength - 1));
            offset += mimeTypeLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            var event = DataOfferOfferEvent(
              mimeType,
            );
            _offerHandler!(event);
          }
          break;
        case 1:
          if (_sourceActionsHandler != null) {
            var offset = 0;
            final sourceActions =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = DataOfferSourceActionsEvent(
              sourceActions,
            );
            _sourceActionsHandler!(event);
          }
          break;
        case 2:
          if (_actionHandler != null) {
            var offset = 0;
            final dndAction =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = DataOfferActionEvent(
              dndAction,
            );
            _actionHandler!(event);
          }
          break;
      }
    }
  }
  
  ///
  ///
  
  enum DataOfferError {
    /// finish request was called untimely
    invalidFinish("invalid_finish", 0),
  
    /// action mask contains invalid values
    invalidActionMask("invalid_action_mask", 1),
  
    /// action argument has an invalid value
    invalidAction("invalid_action", 2),
  
    /// offer doesn't accept this request
    invalidOffer("invalid_offer", 3);
  
    const DataOfferError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "DataOfferError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// a target accepts an offered mime type
  ///
  /// Sent when a target accepts pointer_focus or motion events.  If
  /// a target does not accept any of the offered types, type is NULL.
  ///
  /// Used for feedback during drag-and-drop.
  ///
  class DataSourceTargetEvent {
    /// mime type accepted by the target
    final String mimeType;
  
    DataSourceTargetEvent(
      this.mimeType,
    );
  
    @override
    toString() {
      return "DataSourceTargetEvent (mimeType: $mimeType)";
    }
  }
  
  typedef DataSourceTargetEventHandler = void Function(DataSourceTargetEvent);
  
  /// send the data
  ///
  /// Request for data from the client.  Send the data as the
  /// specified mime type over the passed file descriptor, then
  /// close it.
  ///
  class DataSourceSendEvent {
    /// mime type for the data
    final String mimeType;
  
    /// file descriptor for the data
    final int fd;
  
    DataSourceSendEvent(
      this.mimeType,
      this.fd,
    );
  
    @override
    toString() {
      return "DataSourceSendEvent (mimeType: $mimeType, fd: $fd)";
    }
  }
  
  typedef DataSourceSendEventHandler = void Function(DataSourceSendEvent);
  
  /// selection was cancelled
  ///
  /// This data source is no longer valid. There are several reasons why
  /// this could happen:
  ///
  /// - The data source has been replaced by another data source.
  /// - The drag-and-drop operation was performed, but the drop destination
  /// did not accept any of the mime types offered through
  /// wl_data_source.target.
  /// - The drag-and-drop operation was performed, but the drop destination
  /// did not select any of the actions present in the mask offered through
  /// wl_data_source.action.
  /// - The drag-and-drop operation was performed but didn't happen over a
  /// surface.
  /// - The compositor cancelled the drag-and-drop operation (e.g. compositor
  /// dependent timeouts to avoid stale drag-and-drop transfers).
  ///
  /// The client should clean up and destroy this data source.
  ///
  /// For objects of version 2 or older, wl_data_source.cancelled will
  /// only be emitted if the data source was replaced by another data
  /// source.
  ///
  class DataSourceCancelledEvent {
    DataSourceCancelledEvent();
  
    @override
    toString() {
      return "DataSourceCancelledEvent ()";
    }
  }
  
  typedef DataSourceCancelledEventHandler = void Function(
      DataSourceCancelledEvent);
  
  /// the drag-and-drop operation physically finished
  ///
  /// The user performed the drop action. This event does not indicate
  /// acceptance, wl_data_source.cancelled may still be emitted afterwards
  /// if the drop destination does not accept any mime type.
  ///
  /// However, this event might however not be received if the compositor
  /// cancelled the drag-and-drop operation before this event could happen.
  ///
  /// Note that the data_source may still be used in the future and should
  /// not be destroyed here.
  ///
  class DataSourceDndDropPerformedEvent {
    DataSourceDndDropPerformedEvent();
  
    @override
    toString() {
      return "DataSourceDndDropPerformedEvent ()";
    }
  }
  
  typedef DataSourceDndDropPerformedEventHandler = void Function(
      DataSourceDndDropPerformedEvent);
  
  /// the drag-and-drop operation concluded
  ///
  /// The drop destination finished interoperating with this data
  /// source, so the client is now free to destroy this data source and
  /// free all associated data.
  ///
  /// If the action used to perform the operation was "move", the
  /// source can now delete the transferred data.
  ///
  class DataSourceDndFinishedEvent {
    DataSourceDndFinishedEvent();
  
    @override
    toString() {
      return "DataSourceDndFinishedEvent ()";
    }
  }
  
  typedef DataSourceDndFinishedEventHandler = void Function(
      DataSourceDndFinishedEvent);
  
  /// notify the selected action
  ///
  /// This event indicates the action selected by the compositor after
  /// matching the source/destination side actions. Only one action (or
  /// none) will be offered here.
  ///
  /// This event can be emitted multiple times during the drag-and-drop
  /// operation, mainly in response to destination side changes through
  /// wl_data_offer.set_actions, and as the data device enters/leaves
  /// surfaces.
  ///
  /// It is only possible to receive this event after
  /// wl_data_source.dnd_drop_performed if the drag-and-drop operation
  /// ended in an "ask" action, in which case the final wl_data_source.action
  /// event will happen immediately before wl_data_source.dnd_finished.
  ///
  /// Compositors may also change the selected action on the fly, mainly
  /// in response to keyboard modifier changes during the drag-and-drop
  /// operation.
  ///
  /// The most recent action received is always the valid one. The chosen
  /// action may change alongside negotiation (e.g. an "ask" action can turn
  /// into a "move" operation), so the effects of the final action must
  /// always be applied in wl_data_offer.dnd_finished.
  ///
  /// Clients can trigger cursor surface changes from this point, so
  /// they reflect the current action.
  ///
  class DataSourceActionEvent {
    /// action selected by the compositor
    final int dndAction;
  
    DataSourceActionEvent(
      this.dndAction,
    );
  
    @override
    toString() {
      return "DataSourceActionEvent (dndAction: $dndAction)";
    }
  }
  
  typedef DataSourceActionEventHandler = void Function(DataSourceActionEvent);
  
  /// offer to transfer data
  ///
  /// The wl_data_source object is the source side of a wl_data_offer.
  /// It is created by the source client in a data transfer and
  /// provides a way to describe the offered data and a way to respond
  /// to requests to transfer the data.
  ///
  class DataSource extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 3;
  
    DataSource(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "DataSource {name: 'wl_data_source', id: '$objectId', version: '3',}";
    }
  
    /// add an offered mime type
    ///
    /// This request adds a mime type to the set of mime types
    /// advertised to targets.  Can be called several times to offer
    /// multiple types.
    ///
    /// [mime_type]: mime type offered by the data source
    Result<void, Object> offer(String mimeType) {
      logLn("DataSource::offer  mimeType: $mimeType");
      var arguments = [mimeType];
      var argTypes = <WaylandType>[WaylandType.string];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      final mimeTypeBytes = utf8.encode(mimeType);
      bytesBuilder.add(
          Uint32List.fromList([mimeTypeBytes.length + 1]).buffer.asUint8List());
      bytesBuilder.add(mimeTypeBytes);
      bytesBuilder.add([0]); // Null terminator
      while (bytesBuilder.length % 4 != 0) {
        bytesBuilder.add([0]);
      } // Padding
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataSource::offer: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// destroy the data source
    ///
    /// Destroy the data source.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("DataSource::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataSource::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set the available drag-and-drop actions
    ///
    /// Sets the actions that the source side client supports for this
    /// operation. This request may trigger wl_data_source.action and
    /// wl_data_offer.action events if the compositor needs to change the
    /// selected action.
    ///
    /// The dnd_actions argument must contain only values expressed in the
    /// wl_data_device_manager.dnd_actions enum, otherwise it will result
    /// in a protocol error.
    ///
    /// This request must be made once only, and can only be made on sources
    /// used in drag-and-drop, so it must be performed before
    /// wl_data_device.start_drag. Attempting to use the source other than
    /// for drag-and-drop will raise a protocol error.
    ///
    /// [dnd_actions]: actions supported by the data source
    Result<void, Object> setActions(int dndActions) {
      logLn("DataSource::setActions  dndActions: $dndActions");
      var arguments = [dndActions];
      var argTypes = <WaylandType>[WaylandType.uint];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([dndActions]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataSource::setActions: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// a target accepts an offered mime type
    ///
    /// Sent when a target accepts pointer_focus or motion events.  If
    /// a target does not accept any of the offered types, type is NULL.
    ///
    /// Used for feedback during drag-and-drop.
    ///
    /// Event handler for Target
    /// - [mime_type]: mime type accepted by the target
    void onTarget(DataSourceTargetEventHandler handler) {
      _targetHandler = handler;
    }
  
    DataSourceTargetEventHandler? _targetHandler;
  
    /// send the data
    ///
    /// Request for data from the client.  Send the data as the
    /// specified mime type over the passed file descriptor, then
    /// close it.
    ///
    /// Event handler for Send
    /// - [mime_type]: mime type for the data
    /// - [fd]: file descriptor for the data
    void onSend(DataSourceSendEventHandler handler) {
      _sendHandler = handler;
    }
  
    DataSourceSendEventHandler? _sendHandler;
  
    /// selection was cancelled
    ///
    /// This data source is no longer valid. There are several reasons why
    /// this could happen:
    ///
    /// - The data source has been replaced by another data source.
    /// - The drag-and-drop operation was performed, but the drop destination
    /// did not accept any of the mime types offered through
    /// wl_data_source.target.
    /// - The drag-and-drop operation was performed, but the drop destination
    /// did not select any of the actions present in the mask offered through
    /// wl_data_source.action.
    /// - The drag-and-drop operation was performed but didn't happen over a
    /// surface.
    /// - The compositor cancelled the drag-and-drop operation (e.g. compositor
    /// dependent timeouts to avoid stale drag-and-drop transfers).
    ///
    /// The client should clean up and destroy this data source.
    ///
    /// For objects of version 2 or older, wl_data_source.cancelled will
    /// only be emitted if the data source was replaced by another data
    /// source.
    ///
    /// Event handler for Cancelled
    void onCancelled(DataSourceCancelledEventHandler handler) {
      _cancelledHandler = handler;
    }
  
    DataSourceCancelledEventHandler? _cancelledHandler;
  
    /// the drag-and-drop operation physically finished
    ///
    /// The user performed the drop action. This event does not indicate
    /// acceptance, wl_data_source.cancelled may still be emitted afterwards
    /// if the drop destination does not accept any mime type.
    ///
    /// However, this event might however not be received if the compositor
    /// cancelled the drag-and-drop operation before this event could happen.
    ///
    /// Note that the data_source may still be used in the future and should
    /// not be destroyed here.
    ///
    /// Event handler for DndDropPerformed
    void onDndDropPerformed(DataSourceDndDropPerformedEventHandler handler) {
      _dndDropPerformedHandler = handler;
    }
  
    DataSourceDndDropPerformedEventHandler? _dndDropPerformedHandler;
  
    /// the drag-and-drop operation concluded
    ///
    /// The drop destination finished interoperating with this data
    /// source, so the client is now free to destroy this data source and
    /// free all associated data.
    ///
    /// If the action used to perform the operation was "move", the
    /// source can now delete the transferred data.
    ///
    /// Event handler for DndFinished
    void onDndFinished(DataSourceDndFinishedEventHandler handler) {
      _dndFinishedHandler = handler;
    }
  
    DataSourceDndFinishedEventHandler? _dndFinishedHandler;
  
    /// notify the selected action
    ///
    /// This event indicates the action selected by the compositor after
    /// matching the source/destination side actions. Only one action (or
    /// none) will be offered here.
    ///
    /// This event can be emitted multiple times during the drag-and-drop
    /// operation, mainly in response to destination side changes through
    /// wl_data_offer.set_actions, and as the data device enters/leaves
    /// surfaces.
    ///
    /// It is only possible to receive this event after
    /// wl_data_source.dnd_drop_performed if the drag-and-drop operation
    /// ended in an "ask" action, in which case the final wl_data_source.action
    /// event will happen immediately before wl_data_source.dnd_finished.
    ///
    /// Compositors may also change the selected action on the fly, mainly
    /// in response to keyboard modifier changes during the drag-and-drop
    /// operation.
    ///
    /// The most recent action received is always the valid one. The chosen
    /// action may change alongside negotiation (e.g. an "ask" action can turn
    /// into a "move" operation), so the effects of the final action must
    /// always be applied in wl_data_offer.dnd_finished.
    ///
    /// Clients can trigger cursor surface changes from this point, so
    /// they reflect the current action.
    ///
    /// Event handler for Action
    /// - [dnd_action]: action selected by the compositor
    void onAction(DataSourceActionEventHandler handler) {
      _actionHandler = handler;
    }
  
    DataSourceActionEventHandler? _actionHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("DataSource.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_targetHandler != null) {
            var offset = 0;
            final mimeTypeLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final mimeType =
                utf8.decode(data.sublist(offset, offset + mimeTypeLength - 1));
            offset += mimeTypeLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            var event = DataSourceTargetEvent(
              mimeType,
            );
            _targetHandler!(event);
          }
          break;
        case 1:
          if (fd != -1) {}
          if (_sendHandler != null) {
            var offset = 0;
            final mimeTypeLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final mimeType =
                utf8.decode(data.sublist(offset, offset + mimeTypeLength - 1));
            offset += mimeTypeLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            final fd2 = fd;
            var event = DataSourceSendEvent(
              mimeType,
              fd2,
            );
            _sendHandler!(event);
          }
          break;
        case 2:
          if (_cancelledHandler != null) {
            _cancelledHandler!(DataSourceCancelledEvent());
          }
          break;
        case 3:
          if (_dndDropPerformedHandler != null) {
            _dndDropPerformedHandler!(DataSourceDndDropPerformedEvent());
          }
          break;
        case 4:
          if (_dndFinishedHandler != null) {
            _dndFinishedHandler!(DataSourceDndFinishedEvent());
          }
          break;
        case 5:
          if (_actionHandler != null) {
            var offset = 0;
            final dndAction =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = DataSourceActionEvent(
              dndAction,
            );
            _actionHandler!(event);
          }
          break;
      }
    }
  }
  
  ///
  ///
  
  enum DataSourceError {
    /// action mask contains invalid values
    invalidActionMask("invalid_action_mask", 0),
  
    /// source doesn't accept this request
    invalidSource("invalid_source", 1);
  
    const DataSourceError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "DataSourceError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// introduce a new wl_data_offer
  ///
  /// The data_offer event introduces a new wl_data_offer object,
  /// which will subsequently be used in either the
  /// data_device.enter event (for drag-and-drop) or the
  /// data_device.selection event (for selections).  Immediately
  /// following the data_device.data_offer event, the new data_offer
  /// object will send out data_offer.offer events to describe the
  /// mime types it offers.
  ///
  class DataDeviceDataOfferEvent {
    /// the new data_offer object
    final int id;
  
    DataDeviceDataOfferEvent(
      this.id,
    );
  
    @override
    toString() {
      return "DataDeviceDataOfferEvent (id: $id)";
    }
  }
  
  typedef DataDeviceDataOfferEventHandler = void Function(
      DataDeviceDataOfferEvent);
  
  /// initiate drag-and-drop session
  ///
  /// This event is sent when an active drag-and-drop pointer enters
  /// a surface owned by the client.  The position of the pointer at
  /// enter time is provided by the x and y arguments, in surface-local
  /// coordinates.
  ///
  class DataDeviceEnterEvent {
    /// serial number of the enter event
    final int serial;
  
    /// client surface entered
    final int surface;
  
    /// surface-local x coordinate
    final double x;
  
    /// surface-local y coordinate
    final double y;
  
    /// source data_offer object
    final int id;
  
    DataDeviceEnterEvent(
      this.serial,
      this.surface,
      this.x,
      this.y,
      this.id,
    );
  
    @override
    toString() {
      return "DataDeviceEnterEvent (serial: $serial, surface: $surface, x: $x, y: $y, id: $id)";
    }
  }
  
  typedef DataDeviceEnterEventHandler = void Function(DataDeviceEnterEvent);
  
  /// end drag-and-drop session
  ///
  /// This event is sent when the drag-and-drop pointer leaves the
  /// surface and the session ends.  The client must destroy the
  /// wl_data_offer introduced at enter time at this point.
  ///
  class DataDeviceLeaveEvent {
    DataDeviceLeaveEvent();
  
    @override
    toString() {
      return "DataDeviceLeaveEvent ()";
    }
  }
  
  typedef DataDeviceLeaveEventHandler = void Function(DataDeviceLeaveEvent);
  
  /// drag-and-drop session motion
  ///
  /// This event is sent when the drag-and-drop pointer moves within
  /// the currently focused surface. The new position of the pointer
  /// is provided by the x and y arguments, in surface-local
  /// coordinates.
  ///
  class DataDeviceMotionEvent {
    /// timestamp with millisecond granularity
    final int time;
  
    /// surface-local x coordinate
    final double x;
  
    /// surface-local y coordinate
    final double y;
  
    DataDeviceMotionEvent(
      this.time,
      this.x,
      this.y,
    );
  
    @override
    toString() {
      return "DataDeviceMotionEvent (time: $time, x: $x, y: $y)";
    }
  }
  
  typedef DataDeviceMotionEventHandler = void Function(DataDeviceMotionEvent);
  
  /// end drag-and-drop session successfully
  ///
  /// The event is sent when a drag-and-drop operation is ended
  /// because the implicit grab is removed.
  ///
  /// The drag-and-drop destination is expected to honor the last action
  /// received through wl_data_offer.action, if the resulting action is
  /// "copy" or "move", the destination can still perform
  /// wl_data_offer.receive requests, and is expected to end all
  /// transfers with a wl_data_offer.finish request.
  ///
  /// If the resulting action is "ask", the action will not be considered
  /// final. The drag-and-drop destination is expected to perform one last
  /// wl_data_offer.set_actions request, or wl_data_offer.destroy in order
  /// to cancel the operation.
  ///
  class DataDeviceDropEvent {
    DataDeviceDropEvent();
  
    @override
    toString() {
      return "DataDeviceDropEvent ()";
    }
  }
  
  typedef DataDeviceDropEventHandler = void Function(DataDeviceDropEvent);
  
  /// advertise new selection
  ///
  /// The selection event is sent out to notify the client of a new
  /// wl_data_offer for the selection for this device.  The
  /// data_device.data_offer and the data_offer.offer events are
  /// sent out immediately before this event to introduce the data
  /// offer object.  The selection event is sent to a client
  /// immediately before receiving keyboard focus and when a new
  /// selection is set while the client has keyboard focus.  The
  /// data_offer is valid until a new data_offer or NULL is received
  /// or until the client loses keyboard focus.  Switching surface with
  /// keyboard focus within the same client doesn't mean a new selection
  /// will be sent.  The client must destroy the previous selection
  /// data_offer, if any, upon receiving this event.
  ///
  class DataDeviceSelectionEvent {
    /// selection data_offer object
    final int id;
  
    DataDeviceSelectionEvent(
      this.id,
    );
  
    @override
    toString() {
      return "DataDeviceSelectionEvent (id: $id)";
    }
  }
  
  typedef DataDeviceSelectionEventHandler = void Function(
      DataDeviceSelectionEvent);
  
  /// data transfer device
  ///
  /// There is one wl_data_device per seat which can be obtained
  /// from the global wl_data_device_manager singleton.
  ///
  /// A wl_data_device provides access to inter-client data transfer
  /// mechanisms such as copy-and-paste and drag-and-drop.
  ///
  class DataDevice extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 3;
  
    DataDevice(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "DataDevice {name: 'wl_data_device', id: '$objectId', version: '3',}";
    }
  
    /// start drag-and-drop operation
    ///
    /// This request asks the compositor to start a drag-and-drop
    /// operation on behalf of the client.
    ///
    /// The source argument is the data source that provides the data
    /// for the eventual data transfer. If source is NULL, enter, leave
    /// and motion events are sent only to the client that initiated the
    /// drag and the client is expected to handle the data passing
    /// internally. If source is destroyed, the drag-and-drop session will be
    /// cancelled.
    ///
    /// The origin surface is the surface where the drag originates and
    /// the client must have an active implicit grab that matches the
    /// serial.
    ///
    /// The icon surface is an optional (can be NULL) surface that
    /// provides an icon to be moved around with the cursor.  Initially,
    /// the top-left corner of the icon surface is placed at the cursor
    /// hotspot, but subsequent wl_surface.offset requests can move the
    /// relative position. Attach requests must be confirmed with
    /// wl_surface.commit as usual. The icon surface is given the role of
    /// a drag-and-drop icon. If the icon surface already has another role,
    /// it raises a protocol error.
    ///
    /// The input region is ignored for wl_surfaces with the role of a
    /// drag-and-drop icon.
    ///
    /// The given source may not be used in any further set_selection or
    /// start_drag requests. Attempting to reuse a previously-used source
    /// may send a used_source error.
    ///
    /// [source]: data source for the eventual transfer
    /// [origin]: surface where the drag originates
    /// [icon]: drag-and-drop icon surface
    /// [serial]: serial number of the implicit grab on the origin
    Result<void, Object> startDrag(
        DataSource source, Surface origin, Surface icon, int serial) {
      logLn(
          "DataDevice::startDrag  source: $source origin: $origin icon: $icon serial: $serial");
      var arguments = [source, origin, icon, serial];
      var argTypes = <WaylandType>[
        WaylandType.object,
        WaylandType.object,
        WaylandType.object,
        WaylandType.uint
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([source.objectId]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([origin.objectId]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([icon.objectId]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataDevice::startDrag: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// copy data to the selection
    ///
    /// This request asks the compositor to set the selection
    /// to the data from the source on behalf of the client.
    ///
    /// To unset the selection, set the source to NULL.
    ///
    /// The given source may not be used in any further set_selection or
    /// start_drag requests. Attempting to reuse a previously-used source
    /// may send a used_source error.
    ///
    /// [source]: data source for the selection
    /// [serial]: serial number of the event that triggered this request
    Result<void, Object> setSelection(DataSource source, int serial) {
      logLn("DataDevice::setSelection  source: $source serial: $serial");
      var arguments = [source, serial];
      var argTypes = <WaylandType>[WaylandType.object, WaylandType.uint];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([source.objectId]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataDevice::setSelection: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// destroy data device
    ///
    /// This request destroys the data device.
    ///
    Result<void, Object> release() {
      innerContext.unRegister(this);
      logLn("DataDevice::release ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataDevice::release: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// introduce a new wl_data_offer
    ///
    /// The data_offer event introduces a new wl_data_offer object,
    /// which will subsequently be used in either the
    /// data_device.enter event (for drag-and-drop) or the
    /// data_device.selection event (for selections).  Immediately
    /// following the data_device.data_offer event, the new data_offer
    /// object will send out data_offer.offer events to describe the
    /// mime types it offers.
    ///
    /// Event handler for DataOffer
    /// - [id]: the new data_offer object
    void onDataOffer(DataDeviceDataOfferEventHandler handler) {
      _dataOfferHandler = handler;
    }
  
    DataDeviceDataOfferEventHandler? _dataOfferHandler;
  
    /// initiate drag-and-drop session
    ///
    /// This event is sent when an active drag-and-drop pointer enters
    /// a surface owned by the client.  The position of the pointer at
    /// enter time is provided by the x and y arguments, in surface-local
    /// coordinates.
    ///
    /// Event handler for Enter
    /// - [serial]: serial number of the enter event
    /// - [surface]: client surface entered
    /// - [x]: surface-local x coordinate
    /// - [y]: surface-local y coordinate
    /// - [id]: source data_offer object
    void onEnter(DataDeviceEnterEventHandler handler) {
      _enterHandler = handler;
    }
  
    DataDeviceEnterEventHandler? _enterHandler;
  
    /// end drag-and-drop session
    ///
    /// This event is sent when the drag-and-drop pointer leaves the
    /// surface and the session ends.  The client must destroy the
    /// wl_data_offer introduced at enter time at this point.
    ///
    /// Event handler for Leave
    void onLeave(DataDeviceLeaveEventHandler handler) {
      _leaveHandler = handler;
    }
  
    DataDeviceLeaveEventHandler? _leaveHandler;
  
    /// drag-and-drop session motion
    ///
    /// This event is sent when the drag-and-drop pointer moves within
    /// the currently focused surface. The new position of the pointer
    /// is provided by the x and y arguments, in surface-local
    /// coordinates.
    ///
    /// Event handler for Motion
    /// - [time]: timestamp with millisecond granularity
    /// - [x]: surface-local x coordinate
    /// - [y]: surface-local y coordinate
    void onMotion(DataDeviceMotionEventHandler handler) {
      _motionHandler = handler;
    }
  
    DataDeviceMotionEventHandler? _motionHandler;
  
    /// end drag-and-drop session successfully
    ///
    /// The event is sent when a drag-and-drop operation is ended
    /// because the implicit grab is removed.
    ///
    /// The drag-and-drop destination is expected to honor the last action
    /// received through wl_data_offer.action, if the resulting action is
    /// "copy" or "move", the destination can still perform
    /// wl_data_offer.receive requests, and is expected to end all
    /// transfers with a wl_data_offer.finish request.
    ///
    /// If the resulting action is "ask", the action will not be considered
    /// final. The drag-and-drop destination is expected to perform one last
    /// wl_data_offer.set_actions request, or wl_data_offer.destroy in order
    /// to cancel the operation.
    ///
    /// Event handler for Drop
    void onDrop(DataDeviceDropEventHandler handler) {
      _dropHandler = handler;
    }
  
    DataDeviceDropEventHandler? _dropHandler;
  
    /// advertise new selection
    ///
    /// The selection event is sent out to notify the client of a new
    /// wl_data_offer for the selection for this device.  The
    /// data_device.data_offer and the data_offer.offer events are
    /// sent out immediately before this event to introduce the data
    /// offer object.  The selection event is sent to a client
    /// immediately before receiving keyboard focus and when a new
    /// selection is set while the client has keyboard focus.  The
    /// data_offer is valid until a new data_offer or NULL is received
    /// or until the client loses keyboard focus.  Switching surface with
    /// keyboard focus within the same client doesn't mean a new selection
    /// will be sent.  The client must destroy the previous selection
    /// data_offer, if any, upon receiving this event.
    ///
    /// Event handler for Selection
    /// - [id]: selection data_offer object
    void onSelection(DataDeviceSelectionEventHandler handler) {
      _selectionHandler = handler;
    }
  
    DataDeviceSelectionEventHandler? _selectionHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("DataDevice.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_dataOfferHandler != null) {
            var offset = 0;
            final id = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            var event = DataDeviceDataOfferEvent(
              id,
            );
            _dataOfferHandler!(event);
          }
          break;
        case 1:
          if (_enterHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final surface = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            final x = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final y = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final id = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            var event = DataDeviceEnterEvent(
              serial,
              surface,
              x,
              y,
              id,
            );
            _enterHandler!(event);
          }
          break;
        case 2:
          if (_leaveHandler != null) {
            _leaveHandler!(DataDeviceLeaveEvent());
          }
          break;
        case 3:
          if (_motionHandler != null) {
            var offset = 0;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final x = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final y = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = DataDeviceMotionEvent(
              time,
              x,
              y,
            );
            _motionHandler!(event);
          }
          break;
        case 4:
          if (_dropHandler != null) {
            _dropHandler!(DataDeviceDropEvent());
          }
          break;
        case 5:
          if (_selectionHandler != null) {
            var offset = 0;
            final id = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            var event = DataDeviceSelectionEvent(
              id,
            );
            _selectionHandler!(event);
          }
          break;
      }
    }
  }
  
  ///
  ///
  
  enum DataDeviceError {
    /// given wl_surface has another role
    role("role", 0),
  
    /// source has already been used
    usedSource("used_source", 1);
  
    const DataDeviceError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "DataDeviceError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// data transfer interface
  ///
  /// The wl_data_device_manager is a singleton global object that
  /// provides access to inter-client data transfer mechanisms such as
  /// copy-and-paste and drag-and-drop.  These mechanisms are tied to
  /// a wl_seat and this interface lets a client get a wl_data_device
  /// corresponding to a wl_seat.
  ///
  /// Depending on the version bound, the objects created from the bound
  /// wl_data_device_manager object will have different requirements for
  /// functioning properly. See wl_data_source.set_actions,
  /// wl_data_offer.accept and wl_data_offer.finish for details.
  ///
  class DataDeviceManager extends Proxy {
    final Context innerContext;
    final version = 3;
  
    DataDeviceManager(this.innerContext)
        : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "DataDeviceManager {name: 'wl_data_device_manager', id: '$objectId', version: '3',}";
    }
  
    /// create a new data source
    ///
    /// Create a new data source.
    ///
    /// [id]: data source to create
    Result<DataSource, Object> createDataSource() {
      var id = DataSource(innerContext);
      logLn("DataDeviceManager::createDataSource  id: $id");
      var arguments = [id];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataDeviceManager::createDataSource: $e");
        return Failure(e);
      }
      return Success(id);
    }
  
    /// create a new data device
    ///
    /// Create a new data device for a given seat.
    ///
    /// [id]: data device to create
    /// [seat]: seat associated with the data device
    Result<DataDevice, Object> getDataDevice(Seat seat) {
      var id = DataDevice(innerContext);
      logLn("DataDeviceManager::getDataDevice  id: $id seat: $seat");
      var arguments = [id, seat];
      var argTypes = <WaylandType>[WaylandType.newId, WaylandType.object];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([seat.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in DataDeviceManager::getDataDevice: $e");
        return Failure(e);
      }
      return Success(id);
    }
  }
  
  /// drag and drop actions
  ///
  /// This is a bitmask of the available/preferred actions in a
  /// drag-and-drop operation.
  ///
  /// In the compositor, the selected action is a result of matching the
  /// actions offered by the source and destination sides.  "action" events
  /// with a "none" action will be sent to both source and destination if
  /// there is no match. All further checks will effectively happen on
  /// (source actions ∩ destination actions).
  ///
  /// In addition, compositors may also pick different actions in
  /// reaction to key modifiers being pressed. One common design that
  /// is used in major toolkits (and the behavior recommended for
  /// compositors) is:
  ///
  /// - If no modifiers are pressed, the first match (in bit order)
  /// will be used.
  /// - Pressing Shift selects "move", if enabled in the mask.
  /// - Pressing Control selects "copy", if enabled in the mask.
  ///
  /// Behavior beyond that is considered implementation-dependent.
  /// Compositors may for example bind other modifiers (like Alt/Meta)
  /// or drags initiated with other buttons than BTN_LEFT to specific
  /// actions (e.g. "ask").
  ///
  
  enum DataDeviceManagerDndAction {
    /// no action
    none("none", 0),
  
    /// copy action
    copy("copy", 1),
  
    /// move action
    move("move", 2),
  
    /// ask action
    ask("ask", 4);
  
    const DataDeviceManagerDndAction(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "DataDeviceManagerDndAction {name: $enumName, value: $enumValue}";
    }
  }
  
  /// create desktop-style surfaces
  ///
  /// This interface is implemented by servers that provide
  /// desktop-style user interfaces.
  ///
  /// It allows clients to associate a wl_shell_surface with
  /// a basic surface.
  ///
  /// Note! This protocol is deprecated and not intended for production use.
  /// For desktop-style user interfaces, use xdg_shell. Compositors and clients
  /// should not implement this interface.
  ///
  class Shell extends Proxy {
    final Context innerContext;
    final version = 1;
  
    Shell(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Shell {name: 'wl_shell', id: '$objectId', version: '1',}";
    }
  
    /// create a shell surface from a surface
    ///
    /// Create a shell surface for an existing surface. This gives
    /// the wl_surface the role of a shell surface. If the wl_surface
    /// already has another role, it raises a protocol error.
    ///
    /// Only one shell surface can be associated with a given surface.
    ///
    /// [id]: shell surface to create
    /// [surface]: surface to be given the shell surface role
    Result<ShellSurface, Object> getShellSurface(Surface surface) {
      var id = ShellSurface(innerContext);
      logLn("Shell::getShellSurface  id: $id surface: $surface");
      var arguments = [id, surface];
      var argTypes = <WaylandType>[WaylandType.newId, WaylandType.object];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([surface.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Shell::getShellSurface: $e");
        return Failure(e);
      }
      return Success(id);
    }
  }
  
  ///
  ///
  
  enum ShellError {
    /// given wl_surface has another role
    role("role", 0);
  
    const ShellError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "ShellError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// ping client
  ///
  /// Ping a client to check if it is receiving events and sending
  /// requests. A client is expected to reply with a pong request.
  ///
  class ShellSurfacePingEvent {
    /// serial number of the ping
    final int serial;
  
    ShellSurfacePingEvent(
      this.serial,
    );
  
    @override
    toString() {
      return "ShellSurfacePingEvent (serial: $serial)";
    }
  }
  
  typedef ShellSurfacePingEventHandler = void Function(ShellSurfacePingEvent);
  
  /// suggest resize
  ///
  /// The configure event asks the client to resize its surface.
  ///
  /// The size is a hint, in the sense that the client is free to
  /// ignore it if it doesn't resize, pick a smaller size (to
  /// satisfy aspect ratio or resize in steps of NxM pixels).
  ///
  /// The edges parameter provides a hint about how the surface
  /// was resized. The client may use this information to decide
  /// how to adjust its content to the new size (e.g. a scrolling
  /// area might adjust its content position to leave the viewable
  /// content unmoved).
  ///
  /// The client is free to dismiss all but the last configure
  /// event it received.
  ///
  /// The width and height arguments specify the size of the window
  /// in surface-local coordinates.
  ///
  class ShellSurfaceConfigureEvent {
    /// how the surface was resized
    final int edges;
  
    /// new width of the surface
    final int width;
  
    /// new height of the surface
    final int height;
  
    ShellSurfaceConfigureEvent(
      this.edges,
      this.width,
      this.height,
    );
  
    @override
    toString() {
      return "ShellSurfaceConfigureEvent (edges: $edges, width: $width, height: $height)";
    }
  }
  
  typedef ShellSurfaceConfigureEventHandler = void Function(
      ShellSurfaceConfigureEvent);
  
  /// popup interaction is done
  ///
  /// The popup_done event is sent out when a popup grab is broken,
  /// that is, when the user clicks a surface that doesn't belong
  /// to the client owning the popup surface.
  ///
  class ShellSurfacePopupDoneEvent {
    ShellSurfacePopupDoneEvent();
  
    @override
    toString() {
      return "ShellSurfacePopupDoneEvent ()";
    }
  }
  
  typedef ShellSurfacePopupDoneEventHandler = void Function(
      ShellSurfacePopupDoneEvent);
  
  /// desktop-style metadata interface
  ///
  /// An interface that may be implemented by a wl_surface, for
  /// implementations that provide a desktop-style user interface.
  ///
  /// It provides requests to treat surfaces like toplevel, fullscreen
  /// or popup windows, move, resize or maximize them, associate
  /// metadata like title and class, etc.
  ///
  /// On the server side the object is automatically destroyed when
  /// the related wl_surface is destroyed. On the client side,
  /// wl_shell_surface_destroy() must be called before destroying
  /// the wl_surface object.
  ///
  class ShellSurface extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 1;
  
    ShellSurface(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "ShellSurface {name: 'wl_shell_surface', id: '$objectId', version: '1',}";
    }
  
    /// respond to a ping event
    ///
    /// A client must respond to a ping event with a pong request or
    /// the client may be deemed unresponsive.
    ///
    /// [serial]: serial number of the ping event
    Result<void, Object> pong(int serial) {
      logLn("ShellSurface::pong  serial: $serial");
      var arguments = [serial];
      var argTypes = <WaylandType>[WaylandType.uint];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::pong: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// start an interactive move
    ///
    /// Start a pointer-driven move of the surface.
    ///
    /// This request must be used in response to a button press event.
    /// The server may ignore move requests depending on the state of
    /// the surface (e.g. fullscreen or maximized).
    ///
    /// [seat]: seat whose pointer is used
    /// [serial]: serial number of the implicit grab on the pointer
    Result<void, Object> move(Seat seat, int serial) {
      logLn("ShellSurface::move  seat: $seat serial: $serial");
      var arguments = [seat, serial];
      var argTypes = <WaylandType>[WaylandType.object, WaylandType.uint];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([seat.objectId]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::move: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// start an interactive resize
    ///
    /// Start a pointer-driven resizing of the surface.
    ///
    /// This request must be used in response to a button press event.
    /// The server may ignore resize requests depending on the state of
    /// the surface (e.g. fullscreen or maximized).
    ///
    /// [seat]: seat whose pointer is used
    /// [serial]: serial number of the implicit grab on the pointer
    /// [edges]: which edge or corner is being dragged
    Result<void, Object> resize(Seat seat, int serial, int edges) {
      logLn("ShellSurface::resize  seat: $seat serial: $serial edges: $edges");
      var arguments = [seat, serial, edges];
      var argTypes = <WaylandType>[
        WaylandType.object,
        WaylandType.uint,
        WaylandType.uint
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([seat.objectId]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([edges]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::resize: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// make the surface a toplevel surface
    ///
    /// Map the surface as a toplevel surface.
    ///
    /// A toplevel surface is not fullscreen, maximized or transient.
    ///
    Result<void, Object> setToplevel() {
      logLn("ShellSurface::setToplevel ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 3])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::setToplevel: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// make the surface a transient surface
    ///
    /// Map the surface relative to an existing surface.
    ///
    /// The x and y arguments specify the location of the upper left
    /// corner of the surface relative to the upper left corner of the
    /// parent surface, in surface-local coordinates.
    ///
    /// The flags argument controls details of the transient behaviour.
    ///
    /// [parent]: parent surface
    /// [x]: surface-local x coordinate
    /// [y]: surface-local y coordinate
    /// [flags]: transient surface behavior
    Result<void, Object> setTransient(Surface parent, int x, int y, int flags) {
      logLn(
          "ShellSurface::setTransient  parent: $parent x: $x y: $y flags: $flags");
      var arguments = [parent, x, y, flags];
      var argTypes = <WaylandType>[
        WaylandType.object,
        WaylandType.int,
        WaylandType.int,
        WaylandType.uint
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 4])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([parent.objectId]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([flags]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::setTransient: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// make the surface a fullscreen surface
    ///
    /// Map the surface as a fullscreen surface.
    ///
    /// If an output parameter is given then the surface will be made
    /// fullscreen on that output. If the client does not specify the
    /// output then the compositor will apply its policy - usually
    /// choosing the output on which the surface has the biggest surface
    /// area.
    ///
    /// The client may specify a method to resolve a size conflict
    /// between the output size and the surface size - this is provided
    /// through the method parameter.
    ///
    /// The framerate parameter is used only when the method is set
    /// to "driver", to indicate the preferred framerate. A value of 0
    /// indicates that the client does not care about framerate.  The
    /// framerate is specified in mHz, that is framerate of 60000 is 60Hz.
    ///
    /// A method of "scale" or "driver" implies a scaling operation of
    /// the surface, either via a direct scaling operation or a change of
    /// the output mode. This will override any kind of output scaling, so
    /// that mapping a surface with a buffer size equal to the mode can
    /// fill the screen independent of buffer_scale.
    ///
    /// A method of "fill" means we don't scale up the buffer, however
    /// any output scale is applied. This means that you may run into
    /// an edge case where the application maps a buffer with the same
    /// size of the output mode but buffer_scale 1 (thus making a
    /// surface larger than the output). In this case it is allowed to
    /// downscale the results to fit the screen.
    ///
    /// The compositor must reply to this request with a configure event
    /// with the dimensions for the output on which the surface will
    /// be made fullscreen.
    ///
    /// [method]: method for resolving size conflict
    /// [framerate]: framerate in mHz
    /// [output]: output on which the surface is to be fullscreen
    Result<void, Object> setFullscreen(int method, int framerate, Output output) {
      logLn(
          "ShellSurface::setFullscreen  method: $method framerate: $framerate output: $output");
      var arguments = [method, framerate, output];
      var argTypes = <WaylandType>[
        WaylandType.uint,
        WaylandType.uint,
        WaylandType.object
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 5])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([method]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([framerate]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([output.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::setFullscreen: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// make the surface a popup surface
    ///
    /// Map the surface as a popup.
    ///
    /// A popup surface is a transient surface with an added pointer
    /// grab.
    ///
    /// An existing implicit grab will be changed to owner-events mode,
    /// and the popup grab will continue after the implicit grab ends
    /// (i.e. releasing the mouse button does not cause the popup to
    /// be unmapped).
    ///
    /// The popup grab continues until the window is destroyed or a
    /// mouse button is pressed in any other client's window. A click
    /// in any of the client's surfaces is reported as normal, however,
    /// clicks in other clients' surfaces will be discarded and trigger
    /// the callback.
    ///
    /// The x and y arguments specify the location of the upper left
    /// corner of the surface relative to the upper left corner of the
    /// parent surface, in surface-local coordinates.
    ///
    /// [seat]: seat whose pointer is used
    /// [serial]: serial number of the implicit grab on the pointer
    /// [parent]: parent surface
    /// [x]: surface-local x coordinate
    /// [y]: surface-local y coordinate
    /// [flags]: transient surface behavior
    Result<void, Object> setPopup(
        Seat seat, int serial, Surface parent, int x, int y, int flags) {
      logLn(
          "ShellSurface::setPopup  seat: $seat serial: $serial parent: $parent x: $x y: $y flags: $flags");
      var arguments = [seat, serial, parent, x, y, flags];
      var argTypes = <WaylandType>[
        WaylandType.object,
        WaylandType.uint,
        WaylandType.object,
        WaylandType.int,
        WaylandType.int,
        WaylandType.uint
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 6])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([seat.objectId]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([parent.objectId]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      bytesBuilder.add(Uint32List.fromList([flags]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::setPopup: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// make the surface a maximized surface
    ///
    /// Map the surface as a maximized surface.
    ///
    /// If an output parameter is given then the surface will be
    /// maximized on that output. If the client does not specify the
    /// output then the compositor will apply its policy - usually
    /// choosing the output on which the surface has the biggest surface
    /// area.
    ///
    /// The compositor will reply with a configure event telling
    /// the expected new surface size. The operation is completed
    /// on the next buffer attach to this surface.
    ///
    /// A maximized surface typically fills the entire output it is
    /// bound to, except for desktop elements such as panels. This is
    /// the main difference between a maximized shell surface and a
    /// fullscreen shell surface.
    ///
    /// The details depend on the compositor implementation.
    ///
    /// [output]: output on which the surface is to be maximized
    Result<void, Object> setMaximized(Output output) {
      logLn("ShellSurface::setMaximized  output: $output");
      var arguments = [output];
      var argTypes = <WaylandType>[WaylandType.object];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 7])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([output.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::setMaximized: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set surface title
    ///
    /// Set a short title for the surface.
    ///
    /// This string may be used to identify the surface in a task bar,
    /// window list, or other user interface elements provided by the
    /// compositor.
    ///
    /// The string must be encoded in UTF-8.
    ///
    /// [title]: surface title
    Result<void, Object> setTitle(String title) {
      logLn("ShellSurface::setTitle  title: $title");
      var arguments = [title];
      var argTypes = <WaylandType>[WaylandType.string];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 8])
              .buffer
              .asUint8List());
      final titleBytes = utf8.encode(title);
      bytesBuilder
          .add(Uint32List.fromList([titleBytes.length + 1]).buffer.asUint8List());
      bytesBuilder.add(titleBytes);
      bytesBuilder.add([0]); // Null terminator
      while (bytesBuilder.length % 4 != 0) {
        bytesBuilder.add([0]);
      } // Padding
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::setTitle: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set surface class
    ///
    /// Set a class for the surface.
    ///
    /// The surface class identifies the general class of applications
    /// to which the surface belongs. A common convention is to use the
    /// file name (or the full path if it is a non-standard location) of
    /// the application's .desktop file as the class.
    ///
    /// [class_]: surface class
    Result<void, Object> setClass(String clazz) {
      logLn("ShellSurface::setClass  clazz: $clazz");
      var arguments = [clazz];
      var argTypes = <WaylandType>[WaylandType.string];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 9])
              .buffer
              .asUint8List());
      final clazzBytes = utf8.encode(clazz);
      bytesBuilder
          .add(Uint32List.fromList([clazzBytes.length + 1]).buffer.asUint8List());
      bytesBuilder.add(clazzBytes);
      bytesBuilder.add([0]); // Null terminator
      while (bytesBuilder.length % 4 != 0) {
        bytesBuilder.add([0]);
      } // Padding
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in ShellSurface::setClass: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// ping client
    ///
    /// Ping a client to check if it is receiving events and sending
    /// requests. A client is expected to reply with a pong request.
    ///
    /// Event handler for Ping
    /// - [serial]: serial number of the ping
    void onPing(ShellSurfacePingEventHandler handler) {
      _pingHandler = handler;
    }
  
    ShellSurfacePingEventHandler? _pingHandler;
  
    /// suggest resize
    ///
    /// The configure event asks the client to resize its surface.
    ///
    /// The size is a hint, in the sense that the client is free to
    /// ignore it if it doesn't resize, pick a smaller size (to
    /// satisfy aspect ratio or resize in steps of NxM pixels).
    ///
    /// The edges parameter provides a hint about how the surface
    /// was resized. The client may use this information to decide
    /// how to adjust its content to the new size (e.g. a scrolling
    /// area might adjust its content position to leave the viewable
    /// content unmoved).
    ///
    /// The client is free to dismiss all but the last configure
    /// event it received.
    ///
    /// The width and height arguments specify the size of the window
    /// in surface-local coordinates.
    ///
    /// Event handler for Configure
    /// - [edges]: how the surface was resized
    /// - [width]: new width of the surface
    /// - [height]: new height of the surface
    void onConfigure(ShellSurfaceConfigureEventHandler handler) {
      _configureHandler = handler;
    }
  
    ShellSurfaceConfigureEventHandler? _configureHandler;
  
    /// popup interaction is done
    ///
    /// The popup_done event is sent out when a popup grab is broken,
    /// that is, when the user clicks a surface that doesn't belong
    /// to the client owning the popup surface.
    ///
    /// Event handler for PopupDone
    void onPopupDone(ShellSurfacePopupDoneEventHandler handler) {
      _popupDoneHandler = handler;
    }
  
    ShellSurfacePopupDoneEventHandler? _popupDoneHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("ShellSurface.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_pingHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = ShellSurfacePingEvent(
              serial,
            );
            _pingHandler!(event);
          }
          break;
        case 1:
          if (_configureHandler != null) {
            var offset = 0;
            final edges =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final width =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final height =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = ShellSurfaceConfigureEvent(
              edges,
              width,
              height,
            );
            _configureHandler!(event);
          }
          break;
        case 2:
          if (_popupDoneHandler != null) {
            _popupDoneHandler!(ShellSurfacePopupDoneEvent());
          }
          break;
      }
    }
  }
  
  /// edge values for resizing
  ///
  /// These values are used to indicate which edge of a surface
  /// is being dragged in a resize operation. The server may
  /// use this information to adapt its behavior, e.g. choose
  /// an appropriate cursor image.
  ///
  
  enum ShellSurfaceResize {
    /// no edge
    none("none", 0),
  
    /// top edge
    top("top", 1),
  
    /// bottom edge
    bottom("bottom", 2),
  
    /// left edge
    left("left", 4),
  
    /// top and left edges
    topLeft("top_left", 5),
  
    /// bottom and left edges
    bottomLeft("bottom_left", 6),
  
    /// right edge
    right("right", 8),
  
    /// top and right edges
    topRight("top_right", 9),
  
    /// bottom and right edges
    bottomRight("bottom_right", 10);
  
    const ShellSurfaceResize(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "ShellSurfaceResize {name: $enumName, value: $enumValue}";
    }
  }
  
  /// details of transient behaviour
  ///
  /// These flags specify details of the expected behaviour
  /// of transient surfaces. Used in the set_transient request.
  ///
  
  enum ShellSurfaceTransient {
    /// do not set keyboard focus
    inactive("inactive", 0x1);
  
    const ShellSurfaceTransient(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "ShellSurfaceTransient {name: $enumName, value: $enumValue}";
    }
  }
  
  /// different method to set the surface fullscreen
  ///
  /// Hints to indicate to the compositor how to deal with a conflict
  /// between the dimensions of the surface and the dimensions of the
  /// output. The compositor is free to ignore this parameter.
  ///
  
  enum ShellSurfaceFullscreenMethod {
    /// no preference, apply default policy
    defaulted("default", 0),
  
    /// scale, preserve the surface's aspect ratio and center on output
    scale("scale", 1),
  
    /// switch output mode to the smallest mode that can fit the surface, add black borders to compensate size mismatch
    driver("driver", 2),
  
    /// no upscaling, center on output and add black borders to compensate size mismatch
    fill("fill", 3);
  
    const ShellSurfaceFullscreenMethod(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "ShellSurfaceFullscreenMethod {name: $enumName, value: $enumValue}";
    }
  }
  
  /// surface enters an output
  ///
  /// This is emitted whenever a surface's creation, movement, or resizing
  /// results in some part of it being within the scanout region of an
  /// output.
  ///
  /// Note that a surface may be overlapping with zero or more outputs.
  ///
  class SurfaceEnterEvent {
    /// output entered by the surface
    final int output;
  
    SurfaceEnterEvent(
      this.output,
    );
  
    @override
    toString() {
      return "SurfaceEnterEvent (output: $output)";
    }
  }
  
  typedef SurfaceEnterEventHandler = void Function(SurfaceEnterEvent);
  
  /// surface leaves an output
  ///
  /// This is emitted whenever a surface's creation, movement, or resizing
  /// results in it no longer having any part of it within the scanout region
  /// of an output.
  ///
  /// Clients should not use the number of outputs the surface is on for frame
  /// throttling purposes. The surface might be hidden even if no leave event
  /// has been sent, and the compositor might expect new surface content
  /// updates even if no enter event has been sent. The frame event should be
  /// used instead.
  ///
  class SurfaceLeaveEvent {
    /// output left by the surface
    final int output;
  
    SurfaceLeaveEvent(
      this.output,
    );
  
    @override
    toString() {
      return "SurfaceLeaveEvent (output: $output)";
    }
  }
  
  typedef SurfaceLeaveEventHandler = void Function(SurfaceLeaveEvent);
  
  /// preferred buffer scale for the surface
  ///
  /// This event indicates the preferred buffer scale for this surface. It is
  /// sent whenever the compositor's preference changes.
  ///
  /// Before receiving this event the preferred buffer scale for this surface
  /// is 1.
  ///
  /// It is intended that scaling aware clients use this event to scale their
  /// content and use wl_surface.set_buffer_scale to indicate the scale they
  /// have rendered with. This allows clients to supply a higher detail
  /// buffer.
  ///
  /// The compositor shall emit a scale value greater than 0.
  ///
  class SurfacePreferredBufferScaleEvent {
    /// preferred scaling factor
    final int factor;
  
    SurfacePreferredBufferScaleEvent(
      this.factor,
    );
  
    @override
    toString() {
      return "SurfacePreferredBufferScaleEvent (factor: $factor)";
    }
  }
  
  typedef SurfacePreferredBufferScaleEventHandler = void Function(
      SurfacePreferredBufferScaleEvent);
  
  /// preferred buffer transform for the surface
  ///
  /// This event indicates the preferred buffer transform for this surface.
  /// It is sent whenever the compositor's preference changes.
  ///
  /// Before receiving this event the preferred buffer transform for this
  /// surface is normal.
  ///
  /// Applying this transformation to the surface buffer contents and using
  /// wl_surface.set_buffer_transform might allow the compositor to use the
  /// surface buffer more efficiently.
  ///
  class SurfacePreferredBufferTransformEvent {
    /// preferred transform
    final int transform;
  
    SurfacePreferredBufferTransformEvent(
      this.transform,
    );
  
    @override
    toString() {
      return "SurfacePreferredBufferTransformEvent (transform: $transform)";
    }
  }
  
  typedef SurfacePreferredBufferTransformEventHandler = void Function(
      SurfacePreferredBufferTransformEvent);
  
  /// an onscreen surface
  ///
  /// A surface is a rectangular area that may be displayed on zero
  /// or more outputs, and shown any number of times at the compositor's
  /// discretion. They can present wl_buffers, receive user input, and
  /// define a local coordinate system.
  ///
  /// The size of a surface (and relative positions on it) is described
  /// in surface-local coordinates, which may differ from the buffer
  /// coordinates of the pixel content, in case a buffer_transform
  /// or a buffer_scale is used.
  ///
  /// A surface without a "role" is fairly useless: a compositor does
  /// not know where, when or how to present it. The role is the
  /// purpose of a wl_surface. Examples of roles are a cursor for a
  /// pointer (as set by wl_pointer.set_cursor), a drag icon
  /// (wl_data_device.start_drag), a sub-surface
  /// (wl_subcompositor.get_subsurface), and a window as defined by a
  /// shell protocol (e.g. wl_shell.get_shell_surface).
  ///
  /// A surface can have only one role at a time. Initially a
  /// wl_surface does not have a role. Once a wl_surface is given a
  /// role, it is set permanently for the whole lifetime of the
  /// wl_surface object. Giving the current role again is allowed,
  /// unless explicitly forbidden by the relevant interface
  /// specification.
  ///
  /// Surface roles are given by requests in other interfaces such as
  /// wl_pointer.set_cursor. The request should explicitly mention
  /// that this request gives a role to a wl_surface. Often, this
  /// request also creates a new protocol object that represents the
  /// role and adds additional functionality to wl_surface. When a
  /// client wants to destroy a wl_surface, they must destroy this role
  /// object before the wl_surface, otherwise a defunct_role_object error is
  /// sent.
  ///
  /// Destroying the role object does not remove the role from the
  /// wl_surface, but it may stop the wl_surface from "playing the role".
  /// For instance, if a wl_subsurface object is destroyed, the wl_surface
  /// it was created for will be unmapped and forget its position and
  /// z-order. It is allowed to create a wl_subsurface for the same
  /// wl_surface again, but it is not allowed to use the wl_surface as
  /// a cursor (cursor is a different role than sub-surface, and role
  /// switching is not allowed).
  ///
  class Surface extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 6;
  
    Surface(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Surface {name: 'wl_surface', id: '$objectId', version: '6',}";
    }
  
    /// delete surface
    ///
    /// Deletes the surface and invalidates its object ID.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("Surface::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set the surface contents
    ///
    /// Set a buffer as the content of this surface.
    ///
    /// The new size of the surface is calculated based on the buffer
    /// size transformed by the inverse buffer_transform and the
    /// inverse buffer_scale. This means that at commit time the supplied
    /// buffer size must be an integer multiple of the buffer_scale. If
    /// that's not the case, an invalid_size error is sent.
    ///
    /// The x and y arguments specify the location of the new pending
    /// buffer's upper left corner, relative to the current buffer's upper
    /// left corner, in surface-local coordinates. In other words, the
    /// x and y, combined with the new surface size define in which
    /// directions the surface's size changes. Setting anything other than 0
    /// as x and y arguments is discouraged, and should instead be replaced
    /// with using the separate wl_surface.offset request.
    ///
    /// When the bound wl_surface version is 5 or higher, passing any
    /// non-zero x or y is a protocol violation, and will result in an
    /// 'invalid_offset' error being raised. The x and y arguments are ignored
    /// and do not change the pending state. To achieve equivalent semantics,
    /// use wl_surface.offset.
    ///
    /// Surface contents are double-buffered state, see wl_surface.commit.
    ///
    /// The initial surface contents are void; there is no content.
    /// wl_surface.attach assigns the given wl_buffer as the pending
    /// wl_buffer. wl_surface.commit makes the pending wl_buffer the new
    /// surface contents, and the size of the surface becomes the size
    /// calculated from the wl_buffer, as described above. After commit,
    /// there is no pending buffer until the next attach.
    ///
    /// Committing a pending wl_buffer allows the compositor to read the
    /// pixels in the wl_buffer. The compositor may access the pixels at
    /// any time after the wl_surface.commit request. When the compositor
    /// will not access the pixels anymore, it will send the
    /// wl_buffer.release event. Only after receiving wl_buffer.release,
    /// the client may reuse the wl_buffer. A wl_buffer that has been
    /// attached and then replaced by another attach instead of committed
    /// will not receive a release event, and is not used by the
    /// compositor.
    ///
    /// If a pending wl_buffer has been committed to more than one wl_surface,
    /// the delivery of wl_buffer.release events becomes undefined. A well
    /// behaved client should not rely on wl_buffer.release events in this
    /// case. Alternatively, a client could create multiple wl_buffer objects
    /// from the same backing storage or use wp_linux_buffer_release.
    ///
    /// Destroying the wl_buffer after wl_buffer.release does not change
    /// the surface contents. Destroying the wl_buffer before wl_buffer.release
    /// is allowed as long as the underlying buffer storage isn't re-used (this
    /// can happen e.g. on client process termination). However, if the client
    /// destroys the wl_buffer before receiving the wl_buffer.release event and
    /// mutates the underlying buffer storage, the surface contents become
    /// undefined immediately.
    ///
    /// If wl_surface.attach is sent with a NULL wl_buffer, the
    /// following wl_surface.commit will remove the surface content.
    ///
    /// If a pending wl_buffer has been destroyed, the result is not specified.
    /// Many compositors are known to remove the surface content on the following
    /// wl_surface.commit, but this behaviour is not universal. Clients seeking to
    /// maximise compatibility should not destroy pending buffers and should
    /// ensure that they explicitly remove content from surfaces, even after
    /// destroying buffers.
    ///
    /// [buffer]: buffer of surface contents
    /// [x]: surface-local x coordinate
    /// [y]: surface-local y coordinate
    Result<void, Object> attach(Buffer buffer, int x, int y) {
      logLn("Surface::attach  buffer: $buffer x: $x y: $y");
      var arguments = [buffer, x, y];
      var argTypes = <WaylandType>[
        WaylandType.object,
        WaylandType.int,
        WaylandType.int
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([buffer.objectId]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::attach: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// mark part of the surface damaged
    ///
    /// This request is used to describe the regions where the pending
    /// buffer is different from the current surface contents, and where
    /// the surface therefore needs to be repainted. The compositor
    /// ignores the parts of the damage that fall outside of the surface.
    ///
    /// Damage is double-buffered state, see wl_surface.commit.
    ///
    /// The damage rectangle is specified in surface-local coordinates,
    /// where x and y specify the upper left corner of the damage rectangle.
    ///
    /// The initial value for pending damage is empty: no damage.
    /// wl_surface.damage adds pending damage: the new pending damage
    /// is the union of old pending damage and the given rectangle.
    ///
    /// wl_surface.commit assigns pending damage as the current damage,
    /// and clears pending damage. The server will clear the current
    /// damage as it repaints the surface.
    ///
    /// Note! New clients should not use this request. Instead damage can be
    /// posted with wl_surface.damage_buffer which uses buffer coordinates
    /// instead of surface coordinates.
    ///
    /// [x]: surface-local x coordinate
    /// [y]: surface-local y coordinate
    /// [width]: width of damage rectangle
    /// [height]: height of damage rectangle
    Result<void, Object> damage(int x, int y, int width, int height) {
      logLn("Surface::damage  x: $x y: $y width: $width height: $height");
      var arguments = [x, y, width, height];
      var argTypes = <WaylandType>[
        WaylandType.int,
        WaylandType.int,
        WaylandType.int,
        WaylandType.int
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([width]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([height]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::damage: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// request a frame throttling hint
    ///
    /// Request a notification when it is a good time to start drawing a new
    /// frame, by creating a frame callback. This is useful for throttling
    /// redrawing operations, and driving animations.
    ///
    /// When a client is animating on a wl_surface, it can use the 'frame'
    /// request to get notified when it is a good time to draw and commit the
    /// next frame of animation. If the client commits an update earlier than
    /// that, it is likely that some updates will not make it to the display,
    /// and the client is wasting resources by drawing too often.
    ///
    /// The frame request will take effect on the next wl_surface.commit.
    /// The notification will only be posted for one frame unless
    /// requested again. For a wl_surface, the notifications are posted in
    /// the order the frame requests were committed.
    ///
    /// The server must send the notifications so that a client
    /// will not send excessive updates, while still allowing
    /// the highest possible update rate for clients that wait for the reply
    /// before drawing again. The server should give some time for the client
    /// to draw and commit after sending the frame callback events to let it
    /// hit the next output refresh.
    ///
    /// A server should avoid signaling the frame callbacks if the
    /// surface is not visible in any way, e.g. the surface is off-screen,
    /// or completely obscured by other opaque surfaces.
    ///
    /// The object returned by this request will be destroyed by the
    /// compositor after the callback is fired and as such the client must not
    /// attempt to use it after that point.
    ///
    /// The callback_data passed in the callback is the current time, in
    /// milliseconds, with an undefined base.
    ///
    /// [callback]: callback object for the frame request
    Result<Callback, Object> frame() {
      var callback = Callback(innerContext);
      logLn("Surface::frame  callback: $callback");
      var arguments = [callback];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 3])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([callback.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::frame: $e");
        return Failure(e);
      }
      return Success(callback);
    }
  
    /// set opaque region
    ///
    /// This request sets the region of the surface that contains
    /// opaque content.
    ///
    /// The opaque region is an optimization hint for the compositor
    /// that lets it optimize the redrawing of content behind opaque
    /// regions.  Setting an opaque region is not required for correct
    /// behaviour, but marking transparent content as opaque will result
    /// in repaint artifacts.
    ///
    /// The opaque region is specified in surface-local coordinates.
    ///
    /// The compositor ignores the parts of the opaque region that fall
    /// outside of the surface.
    ///
    /// Opaque region is double-buffered state, see wl_surface.commit.
    ///
    /// wl_surface.set_opaque_region changes the pending opaque region.
    /// wl_surface.commit copies the pending region to the current region.
    /// Otherwise, the pending and current regions are never changed.
    ///
    /// The initial value for an opaque region is empty. Setting the pending
    /// opaque region has copy semantics, and the wl_region object can be
    /// destroyed immediately. A NULL wl_region causes the pending opaque
    /// region to be set to empty.
    ///
    /// [region]: opaque region of the surface
    Result<void, Object> setOpaqueRegion(Region region) {
      logLn("Surface::setOpaqueRegion  region: $region");
      var arguments = [region];
      var argTypes = <WaylandType>[WaylandType.object];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 4])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([region.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::setOpaqueRegion: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set input region
    ///
    /// This request sets the region of the surface that can receive
    /// pointer and touch events.
    ///
    /// Input events happening outside of this region will try the next
    /// surface in the server surface stack. The compositor ignores the
    /// parts of the input region that fall outside of the surface.
    ///
    /// The input region is specified in surface-local coordinates.
    ///
    /// Input region is double-buffered state, see wl_surface.commit.
    ///
    /// wl_surface.set_input_region changes the pending input region.
    /// wl_surface.commit copies the pending region to the current region.
    /// Otherwise the pending and current regions are never changed,
    /// except cursor and icon surfaces are special cases, see
    /// wl_pointer.set_cursor and wl_data_device.start_drag.
    ///
    /// The initial value for an input region is infinite. That means the
    /// whole surface will accept input. Setting the pending input region
    /// has copy semantics, and the wl_region object can be destroyed
    /// immediately. A NULL wl_region causes the input region to be set
    /// to infinite.
    ///
    /// [region]: input region of the surface
    Result<void, Object> setInputRegion(Region region) {
      logLn("Surface::setInputRegion  region: $region");
      var arguments = [region];
      var argTypes = <WaylandType>[WaylandType.object];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 5])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([region.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::setInputRegion: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// commit pending surface state
    ///
    /// Surface state (input, opaque, and damage regions, attached buffers,
    /// etc.) is double-buffered. Protocol requests modify the pending state,
    /// as opposed to the active state in use by the compositor.
    ///
    /// A commit request atomically creates a content update from the pending
    /// state, even if the pending state has not been touched. The content
    /// update is placed in a queue until it becomes active. After commit, the
    /// new pending state is as documented for each related request.
    ///
    /// When the content update is applied, the wl_buffer is applied before all
    /// other state. This means that all coordinates in double-buffered state
    /// are relative to the newly attached wl_buffers, except for
    /// wl_surface.attach itself. If there is no newly attached wl_buffer, the
    /// coordinates are relative to the previous content update.
    ///
    /// All requests that need a commit to become effective are documented
    /// to affect double-buffered state.
    ///
    /// Other interfaces may add further double-buffered surface state.
    ///
    Result<void, Object> commit() {
      logLn("Surface::commit ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 6])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::commit: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// sets the buffer transformation
    ///
    /// This request sets the transformation that the client has already applied
    /// to the content of the buffer. The accepted values for the transform
    /// parameter are the values for wl_output.transform.
    ///
    /// The compositor applies the inverse of this transformation whenever it
    /// uses the buffer contents.
    ///
    /// Buffer transform is double-buffered state, see wl_surface.commit.
    ///
    /// A newly created surface has its buffer transformation set to normal.
    ///
    /// wl_surface.set_buffer_transform changes the pending buffer
    /// transformation. wl_surface.commit copies the pending buffer
    /// transformation to the current one. Otherwise, the pending and current
    /// values are never changed.
    ///
    /// The purpose of this request is to allow clients to render content
    /// according to the output transform, thus permitting the compositor to
    /// use certain optimizations even if the display is rotated. Using
    /// hardware overlays and scanning out a client buffer for fullscreen
    /// surfaces are examples of such optimizations. Those optimizations are
    /// highly dependent on the compositor implementation, so the use of this
    /// request should be considered on a case-by-case basis.
    ///
    /// Note that if the transform value includes 90 or 270 degree rotation,
    /// the width of the buffer will become the surface height and the height
    /// of the buffer will become the surface width.
    ///
    /// If transform is not one of the values from the
    /// wl_output.transform enum the invalid_transform protocol error
    /// is raised.
    ///
    /// [transform]: transform for interpreting buffer contents
    Result<void, Object> setBufferTransform(int transform) {
      logLn("Surface::setBufferTransform  transform: $transform");
      var arguments = [transform];
      var argTypes = <WaylandType>[WaylandType.int];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 7])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([transform]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::setBufferTransform: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// sets the buffer scaling factor
    ///
    /// This request sets an optional scaling factor on how the compositor
    /// interprets the contents of the buffer attached to the window.
    ///
    /// Buffer scale is double-buffered state, see wl_surface.commit.
    ///
    /// A newly created surface has its buffer scale set to 1.
    ///
    /// wl_surface.set_buffer_scale changes the pending buffer scale.
    /// wl_surface.commit copies the pending buffer scale to the current one.
    /// Otherwise, the pending and current values are never changed.
    ///
    /// The purpose of this request is to allow clients to supply higher
    /// resolution buffer data for use on high resolution outputs. It is
    /// intended that you pick the same buffer scale as the scale of the
    /// output that the surface is displayed on. This means the compositor
    /// can avoid scaling when rendering the surface on that output.
    ///
    /// Note that if the scale is larger than 1, then you have to attach
    /// a buffer that is larger (by a factor of scale in each dimension)
    /// than the desired surface size.
    ///
    /// If scale is not greater than 0 the invalid_scale protocol error is
    /// raised.
    ///
    /// [scale]: scale for interpreting buffer contents
    Result<void, Object> setBufferScale(int scale) {
      logLn("Surface::setBufferScale  scale: $scale");
      var arguments = [scale];
      var argTypes = <WaylandType>[WaylandType.int];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 8])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([scale]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::setBufferScale: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// mark part of the surface damaged using buffer coordinates
    ///
    /// This request is used to describe the regions where the pending
    /// buffer is different from the current surface contents, and where
    /// the surface therefore needs to be repainted. The compositor
    /// ignores the parts of the damage that fall outside of the surface.
    ///
    /// Damage is double-buffered state, see wl_surface.commit.
    ///
    /// The damage rectangle is specified in buffer coordinates,
    /// where x and y specify the upper left corner of the damage rectangle.
    ///
    /// The initial value for pending damage is empty: no damage.
    /// wl_surface.damage_buffer adds pending damage: the new pending
    /// damage is the union of old pending damage and the given rectangle.
    ///
    /// wl_surface.commit assigns pending damage as the current damage,
    /// and clears pending damage. The server will clear the current
    /// damage as it repaints the surface.
    ///
    /// This request differs from wl_surface.damage in only one way - it
    /// takes damage in buffer coordinates instead of surface-local
    /// coordinates. While this generally is more intuitive than surface
    /// coordinates, it is especially desirable when using wp_viewport
    /// or when a drawing library (like EGL) is unaware of buffer scale
    /// and buffer transform.
    ///
    /// Note: Because buffer transformation changes and damage requests may
    /// be interleaved in the protocol stream, it is impossible to determine
    /// the actual mapping between surface and buffer damage until
    /// wl_surface.commit time. Therefore, compositors wishing to take both
    /// kinds of damage into account will have to accumulate damage from the
    /// two requests separately and only transform from one to the other
    /// after receiving the wl_surface.commit.
    ///
    /// [x]: buffer-local x coordinate
    /// [y]: buffer-local y coordinate
    /// [width]: width of damage rectangle
    /// [height]: height of damage rectangle
    Result<void, Object> damageBuffer(int x, int y, int width, int height) {
      logLn("Surface::damageBuffer  x: $x y: $y width: $width height: $height");
      var arguments = [x, y, width, height];
      var argTypes = <WaylandType>[
        WaylandType.int,
        WaylandType.int,
        WaylandType.int,
        WaylandType.int
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 9])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([width]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([height]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::damageBuffer: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set the surface contents offset
    ///
    /// The x and y arguments specify the location of the new pending
    /// buffer's upper left corner, relative to the current buffer's upper
    /// left corner, in surface-local coordinates. In other words, the
    /// x and y, combined with the new surface size define in which
    /// directions the surface's size changes.
    ///
    /// Surface location offset is double-buffered state, see
    /// wl_surface.commit.
    ///
    /// This request is semantically equivalent to and the replaces the x and y
    /// arguments in the wl_surface.attach request in wl_surface versions prior
    /// to 5. See wl_surface.attach for details.
    ///
    /// [x]: surface-local x coordinate
    /// [y]: surface-local y coordinate
    Result<void, Object> offset(int x, int y) {
      logLn("Surface::offset  x: $x y: $y");
      var arguments = [x, y];
      var argTypes = <WaylandType>[WaylandType.int, WaylandType.int];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 10])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Surface::offset: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// surface enters an output
    ///
    /// This is emitted whenever a surface's creation, movement, or resizing
    /// results in some part of it being within the scanout region of an
    /// output.
    ///
    /// Note that a surface may be overlapping with zero or more outputs.
    ///
    /// Event handler for Enter
    /// - [output]: output entered by the surface
    void onEnter(SurfaceEnterEventHandler handler) {
      _enterHandler = handler;
    }
  
    SurfaceEnterEventHandler? _enterHandler;
  
    /// surface leaves an output
    ///
    /// This is emitted whenever a surface's creation, movement, or resizing
    /// results in it no longer having any part of it within the scanout region
    /// of an output.
    ///
    /// Clients should not use the number of outputs the surface is on for frame
    /// throttling purposes. The surface might be hidden even if no leave event
    /// has been sent, and the compositor might expect new surface content
    /// updates even if no enter event has been sent. The frame event should be
    /// used instead.
    ///
    /// Event handler for Leave
    /// - [output]: output left by the surface
    void onLeave(SurfaceLeaveEventHandler handler) {
      _leaveHandler = handler;
    }
  
    SurfaceLeaveEventHandler? _leaveHandler;
  
    /// preferred buffer scale for the surface
    ///
    /// This event indicates the preferred buffer scale for this surface. It is
    /// sent whenever the compositor's preference changes.
    ///
    /// Before receiving this event the preferred buffer scale for this surface
    /// is 1.
    ///
    /// It is intended that scaling aware clients use this event to scale their
    /// content and use wl_surface.set_buffer_scale to indicate the scale they
    /// have rendered with. This allows clients to supply a higher detail
    /// buffer.
    ///
    /// The compositor shall emit a scale value greater than 0.
    ///
    /// Event handler for PreferredBufferScale
    /// - [factor]: preferred scaling factor
    void onPreferredBufferScale(SurfacePreferredBufferScaleEventHandler handler) {
      _preferredBufferScaleHandler = handler;
    }
  
    SurfacePreferredBufferScaleEventHandler? _preferredBufferScaleHandler;
  
    /// preferred buffer transform for the surface
    ///
    /// This event indicates the preferred buffer transform for this surface.
    /// It is sent whenever the compositor's preference changes.
    ///
    /// Before receiving this event the preferred buffer transform for this
    /// surface is normal.
    ///
    /// Applying this transformation to the surface buffer contents and using
    /// wl_surface.set_buffer_transform might allow the compositor to use the
    /// surface buffer more efficiently.
    ///
    /// Event handler for PreferredBufferTransform
    /// - [transform]: preferred transform
    void onPreferredBufferTransform(
        SurfacePreferredBufferTransformEventHandler handler) {
      _preferredBufferTransformHandler = handler;
    }
  
    SurfacePreferredBufferTransformEventHandler? _preferredBufferTransformHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Surface.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_enterHandler != null) {
            var offset = 0;
            final output = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            var event = SurfaceEnterEvent(
              output,
            );
            _enterHandler!(event);
          }
          break;
        case 1:
          if (_leaveHandler != null) {
            var offset = 0;
            final output = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            var event = SurfaceLeaveEvent(
              output,
            );
            _leaveHandler!(event);
          }
          break;
        case 2:
          if (_preferredBufferScaleHandler != null) {
            var offset = 0;
            final factor =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = SurfacePreferredBufferScaleEvent(
              factor,
            );
            _preferredBufferScaleHandler!(event);
          }
          break;
        case 3:
          if (_preferredBufferTransformHandler != null) {
            var offset = 0;
            final transform =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = SurfacePreferredBufferTransformEvent(
              transform,
            );
            _preferredBufferTransformHandler!(event);
          }
          break;
      }
    }
  }
  
  /// wl_surface error values
  ///
  /// These errors can be emitted in response to wl_surface requests.
  ///
  
  enum SurfaceError {
    /// buffer scale value is invalid
    invalidScale("invalid_scale", 0),
  
    /// buffer transform value is invalid
    invalidTransform("invalid_transform", 1),
  
    /// buffer size is invalid
    invalidSize("invalid_size", 2),
  
    /// buffer offset is invalid
    invalidOffset("invalid_offset", 3),
  
    /// surface was destroyed before its role object
    defunctRoleObject("defunct_role_object", 4);
  
    const SurfaceError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "SurfaceError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// seat capabilities changed
  ///
  /// This is emitted whenever a seat gains or loses the pointer,
  /// keyboard or touch capabilities.  The argument is a capability
  /// enum containing the complete set of capabilities this seat has.
  ///
  /// When the pointer capability is added, a client may create a
  /// wl_pointer object using the wl_seat.get_pointer request. This object
  /// will receive pointer events until the capability is removed in the
  /// future.
  ///
  /// When the pointer capability is removed, a client should destroy the
  /// wl_pointer objects associated with the seat where the capability was
  /// removed, using the wl_pointer.release request. No further pointer
  /// events will be received on these objects.
  ///
  /// In some compositors, if a seat regains the pointer capability and a
  /// client has a previously obtained wl_pointer object of version 4 or
  /// less, that object may start sending pointer events again. This
  /// behavior is considered a misinterpretation of the intended behavior
  /// and must not be relied upon by the client. wl_pointer objects of
  /// version 5 or later must not send events if created before the most
  /// recent event notifying the client of an added pointer capability.
  ///
  /// The above behavior also applies to wl_keyboard and wl_touch with the
  /// keyboard and touch capabilities, respectively.
  ///
  class SeatCapabilitiesEvent {
    /// capabilities of the seat
    final int capabilities;
  
    SeatCapabilitiesEvent(
      this.capabilities,
    );
  
    @override
    toString() {
      return "SeatCapabilitiesEvent (capabilities: $capabilities)";
    }
  }
  
  typedef SeatCapabilitiesEventHandler = void Function(SeatCapabilitiesEvent);
  
  /// unique identifier for this seat
  ///
  /// In a multi-seat configuration the seat name can be used by clients to
  /// help identify which physical devices the seat represents.
  ///
  /// The seat name is a UTF-8 string with no convention defined for its
  /// contents. Each name is unique among all wl_seat globals. The name is
  /// only guaranteed to be unique for the current compositor instance.
  ///
  /// The same seat names are used for all clients. Thus, the name can be
  /// shared across processes to refer to a specific wl_seat global.
  ///
  /// The name event is sent after binding to the seat global. This event is
  /// only sent once per seat object, and the name does not change over the
  /// lifetime of the wl_seat global.
  ///
  /// Compositors may re-use the same seat name if the wl_seat global is
  /// destroyed and re-created later.
  ///
  class SeatNameEvent {
    /// seat identifier
    final String name;
  
    SeatNameEvent(
      this.name,
    );
  
    @override
    toString() {
      return "SeatNameEvent (name: $name)";
    }
  }
  
  typedef SeatNameEventHandler = void Function(SeatNameEvent);
  
  /// group of input devices
  ///
  /// A seat is a group of keyboards, pointer and touch devices. This
  /// object is published as a global during start up, or when such a
  /// device is hot plugged.  A seat typically has a pointer and
  /// maintains a keyboard focus and a pointer focus.
  ///
  class Seat extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 9;
  
    Seat(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Seat {name: 'wl_seat', id: '$objectId', version: '9',}";
    }
  
    /// return pointer object
    ///
    /// The ID provided will be initialized to the wl_pointer interface
    /// for this seat.
    ///
    /// This request only takes effect if the seat has the pointer
    /// capability, or has had the pointer capability in the past.
    /// It is a protocol violation to issue this request on a seat that has
    /// never had the pointer capability. The missing_capability error will
    /// be sent in this case.
    ///
    /// [id]: seat pointer
    Result<Pointer, Object> getPointer() {
      var id = Pointer(innerContext);
      logLn("Seat::getPointer  id: $id");
      var arguments = [id];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Seat::getPointer: $e");
        return Failure(e);
      }
      return Success(id);
    }
  
    /// return keyboard object
    ///
    /// The ID provided will be initialized to the wl_keyboard interface
    /// for this seat.
    ///
    /// This request only takes effect if the seat has the keyboard
    /// capability, or has had the keyboard capability in the past.
    /// It is a protocol violation to issue this request on a seat that has
    /// never had the keyboard capability. The missing_capability error will
    /// be sent in this case.
    ///
    /// [id]: seat keyboard
    Result<Keyboard, Object> getKeyboard() {
      var id = Keyboard(innerContext);
      logLn("Seat::getKeyboard  id: $id");
      var arguments = [id];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Seat::getKeyboard: $e");
        return Failure(e);
      }
      return Success(id);
    }
  
    /// return touch object
    ///
    /// The ID provided will be initialized to the wl_touch interface
    /// for this seat.
    ///
    /// This request only takes effect if the seat has the touch
    /// capability, or has had the touch capability in the past.
    /// It is a protocol violation to issue this request on a seat that has
    /// never had the touch capability. The missing_capability error will
    /// be sent in this case.
    ///
    /// [id]: seat touch interface
    Result<Touch, Object> getTouch() {
      var id = Touch(innerContext);
      logLn("Seat::getTouch  id: $id");
      var arguments = [id];
      var argTypes = <WaylandType>[WaylandType.newId];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Seat::getTouch: $e");
        return Failure(e);
      }
      return Success(id);
    }
  
    /// release the seat object
    ///
    /// Using this request a client can tell the server that it is not going to
    /// use the seat object anymore.
    ///
    Result<void, Object> release() {
      innerContext.unRegister(this);
      logLn("Seat::release ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 3])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Seat::release: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// seat capabilities changed
    ///
    /// This is emitted whenever a seat gains or loses the pointer,
    /// keyboard or touch capabilities.  The argument is a capability
    /// enum containing the complete set of capabilities this seat has.
    ///
    /// When the pointer capability is added, a client may create a
    /// wl_pointer object using the wl_seat.get_pointer request. This object
    /// will receive pointer events until the capability is removed in the
    /// future.
    ///
    /// When the pointer capability is removed, a client should destroy the
    /// wl_pointer objects associated with the seat where the capability was
    /// removed, using the wl_pointer.release request. No further pointer
    /// events will be received on these objects.
    ///
    /// In some compositors, if a seat regains the pointer capability and a
    /// client has a previously obtained wl_pointer object of version 4 or
    /// less, that object may start sending pointer events again. This
    /// behavior is considered a misinterpretation of the intended behavior
    /// and must not be relied upon by the client. wl_pointer objects of
    /// version 5 or later must not send events if created before the most
    /// recent event notifying the client of an added pointer capability.
    ///
    /// The above behavior also applies to wl_keyboard and wl_touch with the
    /// keyboard and touch capabilities, respectively.
    ///
    /// Event handler for Capabilities
    /// - [capabilities]: capabilities of the seat
    void onCapabilities(SeatCapabilitiesEventHandler handler) {
      _capabilitiesHandler = handler;
    }
  
    SeatCapabilitiesEventHandler? _capabilitiesHandler;
  
    /// unique identifier for this seat
    ///
    /// In a multi-seat configuration the seat name can be used by clients to
    /// help identify which physical devices the seat represents.
    ///
    /// The seat name is a UTF-8 string with no convention defined for its
    /// contents. Each name is unique among all wl_seat globals. The name is
    /// only guaranteed to be unique for the current compositor instance.
    ///
    /// The same seat names are used for all clients. Thus, the name can be
    /// shared across processes to refer to a specific wl_seat global.
    ///
    /// The name event is sent after binding to the seat global. This event is
    /// only sent once per seat object, and the name does not change over the
    /// lifetime of the wl_seat global.
    ///
    /// Compositors may re-use the same seat name if the wl_seat global is
    /// destroyed and re-created later.
    ///
    /// Event handler for Name
    /// - [name]: seat identifier
    void onName(SeatNameEventHandler handler) {
      _nameHandler = handler;
    }
  
    SeatNameEventHandler? _nameHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Seat.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_capabilitiesHandler != null) {
            var offset = 0;
            final capabilities =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = SeatCapabilitiesEvent(
              capabilities,
            );
            _capabilitiesHandler!(event);
          }
          break;
        case 1:
          if (_nameHandler != null) {
            var offset = 0;
            final nameLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final name =
                utf8.decode(data.sublist(offset, offset + nameLength - 1));
            offset += nameLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            var event = SeatNameEvent(
              name,
            );
            _nameHandler!(event);
          }
          break;
      }
    }
  }
  
  /// seat capability bitmask
  ///
  /// This is a bitmask of capabilities this seat has; if a member is
  /// set, then it is present on the seat.
  ///
  
  enum SeatCapability {
    /// the seat has pointer devices
    pointer("pointer", 1),
  
    /// the seat has one or more keyboards
    keyboard("keyboard", 2),
  
    /// the seat has touch devices
    touch("touch", 4);
  
    const SeatCapability(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "SeatCapability {name: $enumName, value: $enumValue}";
    }
  }
  
  /// wl_seat error values
  ///
  /// These errors can be emitted in response to wl_seat requests.
  ///
  
  enum SeatError {
    /// get_pointer, get_keyboard or get_touch called on seat without the matching capability
    missingCapability("missing_capability", 0);
  
    const SeatError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "SeatError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// enter event
  ///
  /// Notification that this seat's pointer is focused on a certain
  /// surface.
  ///
  /// When a seat's focus enters a surface, the pointer image
  /// is undefined and a client should respond to this event by setting
  /// an appropriate pointer image with the set_cursor request.
  ///
  class PointerEnterEvent {
    /// serial number of the enter event
    final int serial;
  
    /// surface entered by the pointer
    final int surface;
  
    /// surface-local x coordinate
    final double surfaceX;
  
    /// surface-local y coordinate
    final double surfaceY;
  
    PointerEnterEvent(
      this.serial,
      this.surface,
      this.surfaceX,
      this.surfaceY,
    );
  
    @override
    toString() {
      return "PointerEnterEvent (serial: $serial, surface: $surface, surfaceX: $surfaceX, surfaceY: $surfaceY)";
    }
  }
  
  typedef PointerEnterEventHandler = void Function(PointerEnterEvent);
  
  /// leave event
  ///
  /// Notification that this seat's pointer is no longer focused on
  /// a certain surface.
  ///
  /// The leave notification is sent before the enter notification
  /// for the new focus.
  ///
  class PointerLeaveEvent {
    /// serial number of the leave event
    final int serial;
  
    /// surface left by the pointer
    final int surface;
  
    PointerLeaveEvent(
      this.serial,
      this.surface,
    );
  
    @override
    toString() {
      return "PointerLeaveEvent (serial: $serial, surface: $surface)";
    }
  }
  
  typedef PointerLeaveEventHandler = void Function(PointerLeaveEvent);
  
  /// pointer motion event
  ///
  /// Notification of pointer location change. The arguments
  /// surface_x and surface_y are the location relative to the
  /// focused surface.
  ///
  class PointerMotionEvent {
    /// timestamp with millisecond granularity
    final int time;
  
    /// surface-local x coordinate
    final double surfaceX;
  
    /// surface-local y coordinate
    final double surfaceY;
  
    PointerMotionEvent(
      this.time,
      this.surfaceX,
      this.surfaceY,
    );
  
    @override
    toString() {
      return "PointerMotionEvent (time: $time, surfaceX: $surfaceX, surfaceY: $surfaceY)";
    }
  }
  
  typedef PointerMotionEventHandler = void Function(PointerMotionEvent);
  
  /// pointer button event
  ///
  /// Mouse button click and release notifications.
  ///
  /// The location of the click is given by the last motion or
  /// enter event.
  /// The time argument is a timestamp with millisecond
  /// granularity, with an undefined base.
  ///
  /// The button is a button code as defined in the Linux kernel's
  /// linux/input-event-codes.h header file, e.g. BTN_LEFT.
  ///
  /// Any 16-bit button code value is reserved for future additions to the
  /// kernel's event code list. All other button codes above 0xFFFF are
  /// currently undefined but may be used in future versions of this
  /// protocol.
  ///
  class PointerButtonEvent {
    /// serial number of the button event
    final int serial;
  
    /// timestamp with millisecond granularity
    final int time;
  
    /// button that produced the event
    final int button;
  
    /// physical state of the button
    final int state;
  
    PointerButtonEvent(
      this.serial,
      this.time,
      this.button,
      this.state,
    );
  
    @override
    toString() {
      return "PointerButtonEvent (serial: $serial, time: $time, button: $button, state: $state)";
    }
  }
  
  typedef PointerButtonEventHandler = void Function(PointerButtonEvent);
  
  /// axis event
  ///
  /// Scroll and other axis notifications.
  ///
  /// For scroll events (vertical and horizontal scroll axes), the
  /// value parameter is the length of a vector along the specified
  /// axis in a coordinate space identical to those of motion events,
  /// representing a relative movement along the specified axis.
  ///
  /// For devices that support movements non-parallel to axes multiple
  /// axis events will be emitted.
  ///
  /// When applicable, for example for touch pads, the server can
  /// choose to emit scroll events where the motion vector is
  /// equivalent to a motion event vector.
  ///
  /// When applicable, a client can transform its content relative to the
  /// scroll distance.
  ///
  class PointerAxisEvent {
    /// timestamp with millisecond granularity
    final int time;
  
    /// axis type
    final int axis;
  
    /// length of vector in surface-local coordinate space
    final double value;
  
    PointerAxisEvent(
      this.time,
      this.axis,
      this.value,
    );
  
    @override
    toString() {
      return "PointerAxisEvent (time: $time, axis: $axis, value: $value)";
    }
  }
  
  typedef PointerAxisEventHandler = void Function(PointerAxisEvent);
  
  /// end of a pointer event sequence
  ///
  /// Indicates the end of a set of events that logically belong together.
  /// A client is expected to accumulate the data in all events within the
  /// frame before proceeding.
  ///
  /// All wl_pointer events before a wl_pointer.frame event belong
  /// logically together. For example, in a diagonal scroll motion the
  /// compositor will send an optional wl_pointer.axis_source event, two
  /// wl_pointer.axis events (horizontal and vertical) and finally a
  /// wl_pointer.frame event. The client may use this information to
  /// calculate a diagonal vector for scrolling.
  ///
  /// When multiple wl_pointer.axis events occur within the same frame,
  /// the motion vector is the combined motion of all events.
  /// When a wl_pointer.axis and a wl_pointer.axis_stop event occur within
  /// the same frame, this indicates that axis movement in one axis has
  /// stopped but continues in the other axis.
  /// When multiple wl_pointer.axis_stop events occur within the same
  /// frame, this indicates that these axes stopped in the same instance.
  ///
  /// A wl_pointer.frame event is sent for every logical event group,
  /// even if the group only contains a single wl_pointer event.
  /// Specifically, a client may get a sequence: motion, frame, button,
  /// frame, axis, frame, axis_stop, frame.
  ///
  /// The wl_pointer.enter and wl_pointer.leave events are logical events
  /// generated by the compositor and not the hardware. These events are
  /// also grouped by a wl_pointer.frame. When a pointer moves from one
  /// surface to another, a compositor should group the
  /// wl_pointer.leave event within the same wl_pointer.frame.
  /// However, a client must not rely on wl_pointer.leave and
  /// wl_pointer.enter being in the same wl_pointer.frame.
  /// Compositor-specific policies may require the wl_pointer.leave and
  /// wl_pointer.enter event being split across multiple wl_pointer.frame
  /// groups.
  ///
  class PointerFrameEvent {
    PointerFrameEvent();
  
    @override
    toString() {
      return "PointerFrameEvent ()";
    }
  }
  
  typedef PointerFrameEventHandler = void Function(PointerFrameEvent);
  
  /// axis source event
  ///
  /// Source information for scroll and other axes.
  ///
  /// This event does not occur on its own. It is sent before a
  /// wl_pointer.frame event and carries the source information for
  /// all events within that frame.
  ///
  /// The source specifies how this event was generated. If the source is
  /// wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be
  /// sent when the user lifts the finger off the device.
  ///
  /// If the source is wl_pointer.axis_source.wheel,
  /// wl_pointer.axis_source.wheel_tilt or
  /// wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may
  /// or may not be sent. Whether a compositor sends an axis_stop event
  /// for these sources is hardware-specific and implementation-dependent;
  /// clients must not rely on receiving an axis_stop event for these
  /// scroll sources and should treat scroll sequences from these scroll
  /// sources as unterminated by default.
  ///
  /// This event is optional. If the source is unknown for a particular
  /// axis event sequence, no event is sent.
  /// Only one wl_pointer.axis_source event is permitted per frame.
  ///
  /// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
  /// not guaranteed.
  ///
  class PointerAxisSourceEvent {
    /// source of the axis event
    final int axisSource;
  
    PointerAxisSourceEvent(
      this.axisSource,
    );
  
    @override
    toString() {
      return "PointerAxisSourceEvent (axisSource: $axisSource)";
    }
  }
  
  typedef PointerAxisSourceEventHandler = void Function(PointerAxisSourceEvent);
  
  /// axis stop event
  ///
  /// Stop notification for scroll and other axes.
  ///
  /// For some wl_pointer.axis_source types, a wl_pointer.axis_stop event
  /// is sent to notify a client that the axis sequence has terminated.
  /// This enables the client to implement kinetic scrolling.
  /// See the wl_pointer.axis_source documentation for information on when
  /// this event may be generated.
  ///
  /// Any wl_pointer.axis events with the same axis_source after this
  /// event should be considered as the start of a new axis motion.
  ///
  /// The timestamp is to be interpreted identical to the timestamp in the
  /// wl_pointer.axis event. The timestamp value may be the same as a
  /// preceding wl_pointer.axis event.
  ///
  class PointerAxisStopEvent {
    /// timestamp with millisecond granularity
    final int time;
  
    /// the axis stopped with this event
    final int axis;
  
    PointerAxisStopEvent(
      this.time,
      this.axis,
    );
  
    @override
    toString() {
      return "PointerAxisStopEvent (time: $time, axis: $axis)";
    }
  }
  
  typedef PointerAxisStopEventHandler = void Function(PointerAxisStopEvent);
  
  /// axis click event
  ///
  /// Discrete step information for scroll and other axes.
  ///
  /// This event carries the axis value of the wl_pointer.axis event in
  /// discrete steps (e.g. mouse wheel clicks).
  ///
  /// This event is deprecated with wl_pointer version 8 - this event is not
  /// sent to clients supporting version 8 or later.
  ///
  /// This event does not occur on its own, it is coupled with a
  /// wl_pointer.axis event that represents this axis value on a
  /// continuous scale. The protocol guarantees that each axis_discrete
  /// event is always followed by exactly one axis event with the same
  /// axis number within the same wl_pointer.frame. Note that the protocol
  /// allows for other events to occur between the axis_discrete and
  /// its coupled axis event, including other axis_discrete or axis
  /// events. A wl_pointer.frame must not contain more than one axis_discrete
  /// event per axis type.
  ///
  /// This event is optional; continuous scrolling devices
  /// like two-finger scrolling on touchpads do not have discrete
  /// steps and do not generate this event.
  ///
  /// The discrete value carries the directional information. e.g. a value
  /// of -2 is two steps towards the negative direction of this axis.
  ///
  /// The axis number is identical to the axis number in the associated
  /// axis event.
  ///
  /// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
  /// not guaranteed.
  ///
  class PointerAxisDiscreteEvent {
    /// axis type
    final int axis;
  
    /// number of steps
    final int discrete;
  
    PointerAxisDiscreteEvent(
      this.axis,
      this.discrete,
    );
  
    @override
    toString() {
      return "PointerAxisDiscreteEvent (axis: $axis, discrete: $discrete)";
    }
  }
  
  typedef PointerAxisDiscreteEventHandler = void Function(
      PointerAxisDiscreteEvent);
  
  /// axis high-resolution scroll event
  ///
  /// Discrete high-resolution scroll information.
  ///
  /// This event carries high-resolution wheel scroll information,
  /// with each multiple of 120 representing one logical scroll step
  /// (a wheel detent). For example, an axis_value120 of 30 is one quarter of
  /// a logical scroll step in the positive direction, a value120 of
  /// -240 are two logical scroll steps in the negative direction within the
  /// same hardware event.
  /// Clients that rely on discrete scrolling should accumulate the
  /// value120 to multiples of 120 before processing the event.
  ///
  /// The value120 must not be zero.
  ///
  /// This event replaces the wl_pointer.axis_discrete event in clients
  /// supporting wl_pointer version 8 or later.
  ///
  /// Where a wl_pointer.axis_source event occurs in the same
  /// wl_pointer.frame, the axis source applies to this event.
  ///
  /// The order of wl_pointer.axis_value120 and wl_pointer.axis_source is
  /// not guaranteed.
  ///
  class PointerAxisValue120Event {
    /// axis type
    final int axis;
  
    /// scroll distance as fraction of 120
    final int value120;
  
    PointerAxisValue120Event(
      this.axis,
      this.value120,
    );
  
    @override
    toString() {
      return "PointerAxisValue120Event (axis: $axis, value120: $value120)";
    }
  }
  
  typedef PointerAxisValue120EventHandler = void Function(
      PointerAxisValue120Event);
  
  /// axis relative physical direction event
  ///
  /// Relative directional information of the entity causing the axis
  /// motion.
  ///
  /// For a wl_pointer.axis event, the wl_pointer.axis_relative_direction
  /// event specifies the movement direction of the entity causing the
  /// wl_pointer.axis event. For example:
  /// - if a user's fingers on a touchpad move down and this
  /// causes a wl_pointer.axis vertical_scroll down event, the physical
  /// direction is 'identical'
  /// - if a user's fingers on a touchpad move down and this causes a
  /// wl_pointer.axis vertical_scroll up scroll up event ('natural
  /// scrolling'), the physical direction is 'inverted'.
  ///
  /// A client may use this information to adjust scroll motion of
  /// components. Specifically, enabling natural scrolling causes the
  /// content to change direction compared to traditional scrolling.
  /// Some widgets like volume control sliders should usually match the
  /// physical direction regardless of whether natural scrolling is
  /// active. This event enables clients to match the scroll direction of
  /// a widget to the physical direction.
  ///
  /// This event does not occur on its own, it is coupled with a
  /// wl_pointer.axis event that represents this axis value.
  /// The protocol guarantees that each axis_relative_direction event is
  /// always followed by exactly one axis event with the same
  /// axis number within the same wl_pointer.frame. Note that the protocol
  /// allows for other events to occur between the axis_relative_direction
  /// and its coupled axis event.
  ///
  /// The axis number is identical to the axis number in the associated
  /// axis event.
  ///
  /// The order of wl_pointer.axis_relative_direction,
  /// wl_pointer.axis_discrete and wl_pointer.axis_source is not
  /// guaranteed.
  ///
  class PointerAxisRelativeDirectionEvent {
    /// axis type
    final int axis;
  
    /// physical direction relative to axis motion
    final int direction;
  
    PointerAxisRelativeDirectionEvent(
      this.axis,
      this.direction,
    );
  
    @override
    toString() {
      return "PointerAxisRelativeDirectionEvent (axis: $axis, direction: $direction)";
    }
  }
  
  typedef PointerAxisRelativeDirectionEventHandler = void Function(
      PointerAxisRelativeDirectionEvent);
  
  /// pointer input device
  ///
  /// The wl_pointer interface represents one or more input devices,
  /// such as mice, which control the pointer location and pointer_focus
  /// of a seat.
  ///
  /// The wl_pointer interface generates motion, enter and leave
  /// events for the surfaces that the pointer is located over,
  /// and button and axis events for button presses, button releases
  /// and scrolling.
  ///
  class Pointer extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 9;
  
    Pointer(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Pointer {name: 'wl_pointer', id: '$objectId', version: '9',}";
    }
  
    /// set the pointer surface
    ///
    /// Set the pointer surface, i.e., the surface that contains the
    /// pointer image (cursor). This request gives the surface the role
    /// of a cursor. If the surface already has another role, it raises
    /// a protocol error.
    ///
    /// The cursor actually changes only if the pointer
    /// focus for this device is one of the requesting client's surfaces
    /// or the surface parameter is the current pointer surface. If
    /// there was a previous surface set with this request it is
    /// replaced. If surface is NULL, the pointer image is hidden.
    ///
    /// The parameters hotspot_x and hotspot_y define the position of
    /// the pointer surface relative to the pointer location. Its
    /// top-left corner is always at (x, y) - (hotspot_x, hotspot_y),
    /// where (x, y) are the coordinates of the pointer location, in
    /// surface-local coordinates.
    ///
    /// On wl_surface.offset requests to the pointer surface, hotspot_x
    /// and hotspot_y are decremented by the x and y parameters
    /// passed to the request. The offset must be applied by
    /// wl_surface.commit as usual.
    ///
    /// The hotspot can also be updated by passing the currently set
    /// pointer surface to this request with new values for hotspot_x
    /// and hotspot_y.
    ///
    /// The input region is ignored for wl_surfaces with the role of
    /// a cursor. When the use as a cursor ends, the wl_surface is
    /// unmapped.
    ///
    /// The serial parameter must match the latest wl_pointer.enter
    /// serial number sent to the client. Otherwise the request will be
    /// ignored.
    ///
    /// [serial]: serial number of the enter event
    /// [surface]: pointer surface
    /// [hotspot_x]: surface-local x coordinate
    /// [hotspot_y]: surface-local y coordinate
    Result<void, Object> setCursor(
        int serial, Surface surface, int hotspotX, int hotspotY) {
      logLn(
          "Pointer::setCursor  serial: $serial surface: $surface hotspotX: $hotspotX hotspotY: $hotspotY");
      var arguments = [serial, surface, hotspotX, hotspotY];
      var argTypes = <WaylandType>[
        WaylandType.uint,
        WaylandType.object,
        WaylandType.int,
        WaylandType.int
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([serial]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([surface.objectId]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([hotspotX]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([hotspotY]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Pointer::setCursor: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// release the pointer object
    ///
    /// Using this request a client can tell the server that it is not going to
    /// use the pointer object anymore.
    ///
    /// This request destroys the pointer proxy object, so clients must not call
    /// wl_pointer_destroy() after using this request.
    ///
    Result<void, Object> release() {
      innerContext.unRegister(this);
      logLn("Pointer::release ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Pointer::release: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// enter event
    ///
    /// Notification that this seat's pointer is focused on a certain
    /// surface.
    ///
    /// When a seat's focus enters a surface, the pointer image
    /// is undefined and a client should respond to this event by setting
    /// an appropriate pointer image with the set_cursor request.
    ///
    /// Event handler for Enter
    /// - [serial]: serial number of the enter event
    /// - [surface]: surface entered by the pointer
    /// - [surface_x]: surface-local x coordinate
    /// - [surface_y]: surface-local y coordinate
    void onEnter(PointerEnterEventHandler handler) {
      _enterHandler = handler;
    }
  
    PointerEnterEventHandler? _enterHandler;
  
    /// leave event
    ///
    /// Notification that this seat's pointer is no longer focused on
    /// a certain surface.
    ///
    /// The leave notification is sent before the enter notification
    /// for the new focus.
    ///
    /// Event handler for Leave
    /// - [serial]: serial number of the leave event
    /// - [surface]: surface left by the pointer
    void onLeave(PointerLeaveEventHandler handler) {
      _leaveHandler = handler;
    }
  
    PointerLeaveEventHandler? _leaveHandler;
  
    /// pointer motion event
    ///
    /// Notification of pointer location change. The arguments
    /// surface_x and surface_y are the location relative to the
    /// focused surface.
    ///
    /// Event handler for Motion
    /// - [time]: timestamp with millisecond granularity
    /// - [surface_x]: surface-local x coordinate
    /// - [surface_y]: surface-local y coordinate
    void onMotion(PointerMotionEventHandler handler) {
      _motionHandler = handler;
    }
  
    PointerMotionEventHandler? _motionHandler;
  
    /// pointer button event
    ///
    /// Mouse button click and release notifications.
    ///
    /// The location of the click is given by the last motion or
    /// enter event.
    /// The time argument is a timestamp with millisecond
    /// granularity, with an undefined base.
    ///
    /// The button is a button code as defined in the Linux kernel's
    /// linux/input-event-codes.h header file, e.g. BTN_LEFT.
    ///
    /// Any 16-bit button code value is reserved for future additions to the
    /// kernel's event code list. All other button codes above 0xFFFF are
    /// currently undefined but may be used in future versions of this
    /// protocol.
    ///
    /// Event handler for Button
    /// - [serial]: serial number of the button event
    /// - [time]: timestamp with millisecond granularity
    /// - [button]: button that produced the event
    /// - [state]: physical state of the button
    void onButton(PointerButtonEventHandler handler) {
      _buttonHandler = handler;
    }
  
    PointerButtonEventHandler? _buttonHandler;
  
    /// axis event
    ///
    /// Scroll and other axis notifications.
    ///
    /// For scroll events (vertical and horizontal scroll axes), the
    /// value parameter is the length of a vector along the specified
    /// axis in a coordinate space identical to those of motion events,
    /// representing a relative movement along the specified axis.
    ///
    /// For devices that support movements non-parallel to axes multiple
    /// axis events will be emitted.
    ///
    /// When applicable, for example for touch pads, the server can
    /// choose to emit scroll events where the motion vector is
    /// equivalent to a motion event vector.
    ///
    /// When applicable, a client can transform its content relative to the
    /// scroll distance.
    ///
    /// Event handler for Axis
    /// - [time]: timestamp with millisecond granularity
    /// - [axis]: axis type
    /// - [value]: length of vector in surface-local coordinate space
    void onAxis(PointerAxisEventHandler handler) {
      _axisHandler = handler;
    }
  
    PointerAxisEventHandler? _axisHandler;
  
    /// end of a pointer event sequence
    ///
    /// Indicates the end of a set of events that logically belong together.
    /// A client is expected to accumulate the data in all events within the
    /// frame before proceeding.
    ///
    /// All wl_pointer events before a wl_pointer.frame event belong
    /// logically together. For example, in a diagonal scroll motion the
    /// compositor will send an optional wl_pointer.axis_source event, two
    /// wl_pointer.axis events (horizontal and vertical) and finally a
    /// wl_pointer.frame event. The client may use this information to
    /// calculate a diagonal vector for scrolling.
    ///
    /// When multiple wl_pointer.axis events occur within the same frame,
    /// the motion vector is the combined motion of all events.
    /// When a wl_pointer.axis and a wl_pointer.axis_stop event occur within
    /// the same frame, this indicates that axis movement in one axis has
    /// stopped but continues in the other axis.
    /// When multiple wl_pointer.axis_stop events occur within the same
    /// frame, this indicates that these axes stopped in the same instance.
    ///
    /// A wl_pointer.frame event is sent for every logical event group,
    /// even if the group only contains a single wl_pointer event.
    /// Specifically, a client may get a sequence: motion, frame, button,
    /// frame, axis, frame, axis_stop, frame.
    ///
    /// The wl_pointer.enter and wl_pointer.leave events are logical events
    /// generated by the compositor and not the hardware. These events are
    /// also grouped by a wl_pointer.frame. When a pointer moves from one
    /// surface to another, a compositor should group the
    /// wl_pointer.leave event within the same wl_pointer.frame.
    /// However, a client must not rely on wl_pointer.leave and
    /// wl_pointer.enter being in the same wl_pointer.frame.
    /// Compositor-specific policies may require the wl_pointer.leave and
    /// wl_pointer.enter event being split across multiple wl_pointer.frame
    /// groups.
    ///
    /// Event handler for Frame
    void onFrame(PointerFrameEventHandler handler) {
      _frameHandler = handler;
    }
  
    PointerFrameEventHandler? _frameHandler;
  
    /// axis source event
    ///
    /// Source information for scroll and other axes.
    ///
    /// This event does not occur on its own. It is sent before a
    /// wl_pointer.frame event and carries the source information for
    /// all events within that frame.
    ///
    /// The source specifies how this event was generated. If the source is
    /// wl_pointer.axis_source.finger, a wl_pointer.axis_stop event will be
    /// sent when the user lifts the finger off the device.
    ///
    /// If the source is wl_pointer.axis_source.wheel,
    /// wl_pointer.axis_source.wheel_tilt or
    /// wl_pointer.axis_source.continuous, a wl_pointer.axis_stop event may
    /// or may not be sent. Whether a compositor sends an axis_stop event
    /// for these sources is hardware-specific and implementation-dependent;
    /// clients must not rely on receiving an axis_stop event for these
    /// scroll sources and should treat scroll sequences from these scroll
    /// sources as unterminated by default.
    ///
    /// This event is optional. If the source is unknown for a particular
    /// axis event sequence, no event is sent.
    /// Only one wl_pointer.axis_source event is permitted per frame.
    ///
    /// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
    /// not guaranteed.
    ///
    /// Event handler for AxisSource
    /// - [axis_source]: source of the axis event
    void onAxisSource(PointerAxisSourceEventHandler handler) {
      _axisSourceHandler = handler;
    }
  
    PointerAxisSourceEventHandler? _axisSourceHandler;
  
    /// axis stop event
    ///
    /// Stop notification for scroll and other axes.
    ///
    /// For some wl_pointer.axis_source types, a wl_pointer.axis_stop event
    /// is sent to notify a client that the axis sequence has terminated.
    /// This enables the client to implement kinetic scrolling.
    /// See the wl_pointer.axis_source documentation for information on when
    /// this event may be generated.
    ///
    /// Any wl_pointer.axis events with the same axis_source after this
    /// event should be considered as the start of a new axis motion.
    ///
    /// The timestamp is to be interpreted identical to the timestamp in the
    /// wl_pointer.axis event. The timestamp value may be the same as a
    /// preceding wl_pointer.axis event.
    ///
    /// Event handler for AxisStop
    /// - [time]: timestamp with millisecond granularity
    /// - [axis]: the axis stopped with this event
    void onAxisStop(PointerAxisStopEventHandler handler) {
      _axisStopHandler = handler;
    }
  
    PointerAxisStopEventHandler? _axisStopHandler;
  
    /// axis click event
    ///
    /// Discrete step information for scroll and other axes.
    ///
    /// This event carries the axis value of the wl_pointer.axis event in
    /// discrete steps (e.g. mouse wheel clicks).
    ///
    /// This event is deprecated with wl_pointer version 8 - this event is not
    /// sent to clients supporting version 8 or later.
    ///
    /// This event does not occur on its own, it is coupled with a
    /// wl_pointer.axis event that represents this axis value on a
    /// continuous scale. The protocol guarantees that each axis_discrete
    /// event is always followed by exactly one axis event with the same
    /// axis number within the same wl_pointer.frame. Note that the protocol
    /// allows for other events to occur between the axis_discrete and
    /// its coupled axis event, including other axis_discrete or axis
    /// events. A wl_pointer.frame must not contain more than one axis_discrete
    /// event per axis type.
    ///
    /// This event is optional; continuous scrolling devices
    /// like two-finger scrolling on touchpads do not have discrete
    /// steps and do not generate this event.
    ///
    /// The discrete value carries the directional information. e.g. a value
    /// of -2 is two steps towards the negative direction of this axis.
    ///
    /// The axis number is identical to the axis number in the associated
    /// axis event.
    ///
    /// The order of wl_pointer.axis_discrete and wl_pointer.axis_source is
    /// not guaranteed.
    ///
    /// Event handler for AxisDiscrete
    /// - [axis]: axis type
    /// - [discrete]: number of steps
    void onAxisDiscrete(PointerAxisDiscreteEventHandler handler) {
      _axisDiscreteHandler = handler;
    }
  
    PointerAxisDiscreteEventHandler? _axisDiscreteHandler;
  
    /// axis high-resolution scroll event
    ///
    /// Discrete high-resolution scroll information.
    ///
    /// This event carries high-resolution wheel scroll information,
    /// with each multiple of 120 representing one logical scroll step
    /// (a wheel detent). For example, an axis_value120 of 30 is one quarter of
    /// a logical scroll step in the positive direction, a value120 of
    /// -240 are two logical scroll steps in the negative direction within the
    /// same hardware event.
    /// Clients that rely on discrete scrolling should accumulate the
    /// value120 to multiples of 120 before processing the event.
    ///
    /// The value120 must not be zero.
    ///
    /// This event replaces the wl_pointer.axis_discrete event in clients
    /// supporting wl_pointer version 8 or later.
    ///
    /// Where a wl_pointer.axis_source event occurs in the same
    /// wl_pointer.frame, the axis source applies to this event.
    ///
    /// The order of wl_pointer.axis_value120 and wl_pointer.axis_source is
    /// not guaranteed.
    ///
    /// Event handler for AxisValue120
    /// - [axis]: axis type
    /// - [value120]: scroll distance as fraction of 120
    void onAxisValue120(PointerAxisValue120EventHandler handler) {
      _axisValue120Handler = handler;
    }
  
    PointerAxisValue120EventHandler? _axisValue120Handler;
  
    /// axis relative physical direction event
    ///
    /// Relative directional information of the entity causing the axis
    /// motion.
    ///
    /// For a wl_pointer.axis event, the wl_pointer.axis_relative_direction
    /// event specifies the movement direction of the entity causing the
    /// wl_pointer.axis event. For example:
    /// - if a user's fingers on a touchpad move down and this
    /// causes a wl_pointer.axis vertical_scroll down event, the physical
    /// direction is 'identical'
    /// - if a user's fingers on a touchpad move down and this causes a
    /// wl_pointer.axis vertical_scroll up scroll up event ('natural
    /// scrolling'), the physical direction is 'inverted'.
    ///
    /// A client may use this information to adjust scroll motion of
    /// components. Specifically, enabling natural scrolling causes the
    /// content to change direction compared to traditional scrolling.
    /// Some widgets like volume control sliders should usually match the
    /// physical direction regardless of whether natural scrolling is
    /// active. This event enables clients to match the scroll direction of
    /// a widget to the physical direction.
    ///
    /// This event does not occur on its own, it is coupled with a
    /// wl_pointer.axis event that represents this axis value.
    /// The protocol guarantees that each axis_relative_direction event is
    /// always followed by exactly one axis event with the same
    /// axis number within the same wl_pointer.frame. Note that the protocol
    /// allows for other events to occur between the axis_relative_direction
    /// and its coupled axis event.
    ///
    /// The axis number is identical to the axis number in the associated
    /// axis event.
    ///
    /// The order of wl_pointer.axis_relative_direction,
    /// wl_pointer.axis_discrete and wl_pointer.axis_source is not
    /// guaranteed.
    ///
    /// Event handler for AxisRelativeDirection
    /// - [axis]: axis type
    /// - [direction]: physical direction relative to axis motion
    void onAxisRelativeDirection(
        PointerAxisRelativeDirectionEventHandler handler) {
      _axisRelativeDirectionHandler = handler;
    }
  
    PointerAxisRelativeDirectionEventHandler? _axisRelativeDirectionHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Pointer.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_enterHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final surface = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            final surfaceX = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final surfaceY = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = PointerEnterEvent(
              serial,
              surface,
              surfaceX,
              surfaceY,
            );
            _enterHandler!(event);
          }
          break;
        case 1:
          if (_leaveHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final surface = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            var event = PointerLeaveEvent(
              serial,
              surface,
            );
            _leaveHandler!(event);
          }
          break;
        case 2:
          if (_motionHandler != null) {
            var offset = 0;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final surfaceX = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final surfaceY = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = PointerMotionEvent(
              time,
              surfaceX,
              surfaceY,
            );
            _motionHandler!(event);
          }
          break;
        case 3:
          if (_buttonHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final button =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final state =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = PointerButtonEvent(
              serial,
              time,
              button,
              state,
            );
            _buttonHandler!(event);
          }
          break;
        case 4:
          if (_axisHandler != null) {
            var offset = 0;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final axis =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final value = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = PointerAxisEvent(
              time,
              axis,
              value,
            );
            _axisHandler!(event);
          }
          break;
        case 5:
          if (_frameHandler != null) {
            _frameHandler!(PointerFrameEvent());
          }
          break;
        case 6:
          if (_axisSourceHandler != null) {
            var offset = 0;
            final axisSource =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = PointerAxisSourceEvent(
              axisSource,
            );
            _axisSourceHandler!(event);
          }
          break;
        case 7:
          if (_axisStopHandler != null) {
            var offset = 0;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final axis =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = PointerAxisStopEvent(
              time,
              axis,
            );
            _axisStopHandler!(event);
          }
          break;
        case 8:
          if (_axisDiscreteHandler != null) {
            var offset = 0;
            final axis =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final discrete =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = PointerAxisDiscreteEvent(
              axis,
              discrete,
            );
            _axisDiscreteHandler!(event);
          }
          break;
        case 9:
          if (_axisValue120Handler != null) {
            var offset = 0;
            final axis =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final value120 =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = PointerAxisValue120Event(
              axis,
              value120,
            );
            _axisValue120Handler!(event);
          }
          break;
        case 10:
          if (_axisRelativeDirectionHandler != null) {
            var offset = 0;
            final axis =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final direction =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = PointerAxisRelativeDirectionEvent(
              axis,
              direction,
            );
            _axisRelativeDirectionHandler!(event);
          }
          break;
      }
    }
  }
  
  ///
  ///
  
  enum PointerError {
    /// given wl_surface has another role
    role("role", 0);
  
    const PointerError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "PointerError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// physical button state
  ///
  /// Describes the physical state of a button that produced the button
  /// event.
  ///
  
  enum PointerButtonState {
    /// the button is not pressed
    released("released", 0),
  
    /// the button is pressed
    pressed("pressed", 1);
  
    const PointerButtonState(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "PointerButtonState {name: $enumName, value: $enumValue}";
    }
  }
  
  /// axis types
  ///
  /// Describes the axis types of scroll events.
  ///
  
  enum PointerAxis {
    /// vertical axis
    verticalScroll("vertical_scroll", 0),
  
    /// horizontal axis
    horizontalScroll("horizontal_scroll", 1);
  
    const PointerAxis(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "PointerAxis {name: $enumName, value: $enumValue}";
    }
  }
  
  /// axis source types
  ///
  /// Describes the source types for axis events. This indicates to the
  /// client how an axis event was physically generated; a client may
  /// adjust the user interface accordingly. For example, scroll events
  /// from a "finger" source may be in a smooth coordinate space with
  /// kinetic scrolling whereas a "wheel" source may be in discrete steps
  /// of a number of lines.
  ///
  /// The "continuous" axis source is a device generating events in a
  /// continuous coordinate space, but using something other than a
  /// finger. One example for this source is button-based scrolling where
  /// the vertical motion of a device is converted to scroll events while
  /// a button is held down.
  ///
  /// The "wheel tilt" axis source indicates that the actual device is a
  /// wheel but the scroll event is not caused by a rotation but a
  /// (usually sideways) tilt of the wheel.
  ///
  
  enum PointerAxisSource {
    /// a physical wheel rotation
    wheel("wheel", 0),
  
    /// finger on a touch surface
    finger("finger", 1),
  
    /// continuous coordinate space
    continuous("continuous", 2),
  
    /// a physical wheel tilt
    wheelTilt("wheel_tilt", 3);
  
    const PointerAxisSource(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "PointerAxisSource {name: $enumName, value: $enumValue}";
    }
  }
  
  /// axis relative direction
  ///
  /// This specifies the direction of the physical motion that caused a
  /// wl_pointer.axis event, relative to the wl_pointer.axis direction.
  ///
  
  enum PointerAxisRelativeDirection {
    /// physical motion matches axis direction
    identical("identical", 0),
  
    /// physical motion is the inverse of the axis direction
    inverted("inverted", 1);
  
    const PointerAxisRelativeDirection(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "PointerAxisRelativeDirection {name: $enumName, value: $enumValue}";
    }
  }
  
  /// keyboard mapping
  ///
  /// This event provides a file descriptor to the client which can be
  /// memory-mapped in read-only mode to provide a keyboard mapping
  /// description.
  ///
  /// From version 7 onwards, the fd must be mapped with MAP_PRIVATE by
  /// the recipient, as MAP_SHARED may fail.
  ///
  class KeyboardKeymapEvent {
    /// keymap format
    final int format;
  
    /// keymap file descriptor
    final int fd;
  
    /// keymap size, in bytes
    final int size;
  
    KeyboardKeymapEvent(
      this.format,
      this.fd,
      this.size,
    );
  
    @override
    toString() {
      return "KeyboardKeymapEvent (format: $format, fd: $fd, size: $size)";
    }
  }
  
  typedef KeyboardKeymapEventHandler = void Function(KeyboardKeymapEvent);
  
  /// enter event
  ///
  /// Notification that this seat's keyboard focus is on a certain
  /// surface.
  ///
  /// The compositor must send the wl_keyboard.modifiers event after this
  /// event.
  ///
  /// In the wl_keyboard logical state, this event sets the active surface to
  /// the surface argument and the keys currently logically down to the keys
  /// in the keys argument. The compositor must not send this event if the
  /// wl_keyboard already had an active surface immediately before this event.
  ///
  class KeyboardEnterEvent {
    /// serial number of the enter event
    final int serial;
  
    /// surface gaining keyboard focus
    final int surface;
  
    /// the keys currently logically down
    final List<int> keys;
  
    KeyboardEnterEvent(
      this.serial,
      this.surface,
      this.keys,
    );
  
    @override
    toString() {
      return "KeyboardEnterEvent (serial: $serial, surface: $surface, keys: $keys)";
    }
  }
  
  typedef KeyboardEnterEventHandler = void Function(KeyboardEnterEvent);
  
  /// leave event
  ///
  /// Notification that this seat's keyboard focus is no longer on
  /// a certain surface.
  ///
  /// The leave notification is sent before the enter notification
  /// for the new focus.
  ///
  /// In the wl_keyboard logical state, this event resets all values to their
  /// defaults. The compositor must not send this event if the active surface
  /// of the wl_keyboard was not equal to the surface argument immediately
  /// before this event.
  ///
  class KeyboardLeaveEvent {
    /// serial number of the leave event
    final int serial;
  
    /// surface that lost keyboard focus
    final int surface;
  
    KeyboardLeaveEvent(
      this.serial,
      this.surface,
    );
  
    @override
    toString() {
      return "KeyboardLeaveEvent (serial: $serial, surface: $surface)";
    }
  }
  
  typedef KeyboardLeaveEventHandler = void Function(KeyboardLeaveEvent);
  
  /// key event
  ///
  /// A key was pressed or released.
  /// The time argument is a timestamp with millisecond
  /// granularity, with an undefined base.
  ///
  /// The key is a platform-specific key code that can be interpreted
  /// by feeding it to the keyboard mapping (see the keymap event).
  ///
  /// If this event produces a change in modifiers, then the resulting
  /// wl_keyboard.modifiers event must be sent after this event.
  ///
  /// In the wl_keyboard logical state, this event adds the key to the keys
  /// currently logically down (if the state argument is pressed) or removes
  /// the key from the keys currently logically down (if the state argument is
  /// released). The compositor must not send this event if the wl_keyboard
  /// did not have an active surface immediately before this event. The
  /// compositor must not send this event if state is pressed (resp. released)
  /// and the key was already logically down (resp. was not logically down)
  /// immediately before this event.
  ///
  class KeyboardKeyEvent {
    /// serial number of the key event
    final int serial;
  
    /// timestamp with millisecond granularity
    final int time;
  
    /// key that produced the event
    final int key;
  
    /// physical state of the key
    final int state;
  
    KeyboardKeyEvent(
      this.serial,
      this.time,
      this.key,
      this.state,
    );
  
    @override
    toString() {
      return "KeyboardKeyEvent (serial: $serial, time: $time, key: $key, state: $state)";
    }
  }
  
  typedef KeyboardKeyEventHandler = void Function(KeyboardKeyEvent);
  
  /// modifier and group state
  ///
  /// Notifies clients that the modifier and/or group state has
  /// changed, and it should update its local state.
  ///
  /// The compositor may send this event without a surface of the client
  /// having keyboard focus, for example to tie modifier information to
  /// pointer focus instead. If a modifier event with pressed modifiers is sent
  /// without a prior enter event, the client can assume the modifier state is
  /// valid until it receives the next wl_keyboard.modifiers event. In order to
  /// reset the modifier state again, the compositor can send a
  /// wl_keyboard.modifiers event with no pressed modifiers.
  ///
  /// In the wl_keyboard logical state, this event updates the modifiers and
  /// group.
  ///
  class KeyboardModifiersEvent {
    /// serial number of the modifiers event
    final int serial;
  
    /// depressed modifiers
    final int modsDepressed;
  
    /// latched modifiers
    final int modsLatched;
  
    /// locked modifiers
    final int modsLocked;
  
    /// keyboard layout
    final int group;
  
    KeyboardModifiersEvent(
      this.serial,
      this.modsDepressed,
      this.modsLatched,
      this.modsLocked,
      this.group,
    );
  
    @override
    toString() {
      return "KeyboardModifiersEvent (serial: $serial, modsDepressed: $modsDepressed, modsLatched: $modsLatched, modsLocked: $modsLocked, group: $group)";
    }
  }
  
  typedef KeyboardModifiersEventHandler = void Function(KeyboardModifiersEvent);
  
  /// repeat rate and delay
  ///
  /// Informs the client about the keyboard's repeat rate and delay.
  ///
  /// This event is sent as soon as the wl_keyboard object has been created,
  /// and is guaranteed to be received by the client before any key press
  /// event.
  ///
  /// Negative values for either rate or delay are illegal. A rate of zero
  /// will disable any repeating (regardless of the value of delay).
  ///
  /// This event can be sent later on as well with a new value if necessary,
  /// so clients should continue listening for the event past the creation
  /// of wl_keyboard.
  ///
  class KeyboardRepeatInfoEvent {
    /// the rate of repeating keys in characters per second
    final int rate;
  
    /// delay in milliseconds since key down until repeating starts
    final int delay;
  
    KeyboardRepeatInfoEvent(
      this.rate,
      this.delay,
    );
  
    @override
    toString() {
      return "KeyboardRepeatInfoEvent (rate: $rate, delay: $delay)";
    }
  }
  
  typedef KeyboardRepeatInfoEventHandler = void Function(KeyboardRepeatInfoEvent);
  
  /// keyboard input device
  ///
  /// The wl_keyboard interface represents one or more keyboards
  /// associated with a seat.
  ///
  /// Each wl_keyboard has the following logical state:
  ///
  /// - an active surface (possibly null),
  /// - the keys currently logically down,
  /// - the active modifiers,
  /// - the active group.
  ///
  /// By default, the active surface is null, the keys currently logically down
  /// are empty, the active modifiers and the active group are 0.
  ///
  class Keyboard extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 9;
  
    Keyboard(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Keyboard {name: 'wl_keyboard', id: '$objectId', version: '9',}";
    }
  
    /// release the keyboard object
    ///
    Result<void, Object> release() {
      innerContext.unRegister(this);
      logLn("Keyboard::release ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Keyboard::release: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// keyboard mapping
    ///
    /// This event provides a file descriptor to the client which can be
    /// memory-mapped in read-only mode to provide a keyboard mapping
    /// description.
    ///
    /// From version 7 onwards, the fd must be mapped with MAP_PRIVATE by
    /// the recipient, as MAP_SHARED may fail.
    ///
    /// Event handler for Keymap
    /// - [format]: keymap format
    /// - [fd]: keymap file descriptor
    /// - [size]: keymap size, in bytes
    void onKeymap(KeyboardKeymapEventHandler handler) {
      _keymapHandler = handler;
    }
  
    KeyboardKeymapEventHandler? _keymapHandler;
  
    /// enter event
    ///
    /// Notification that this seat's keyboard focus is on a certain
    /// surface.
    ///
    /// The compositor must send the wl_keyboard.modifiers event after this
    /// event.
    ///
    /// In the wl_keyboard logical state, this event sets the active surface to
    /// the surface argument and the keys currently logically down to the keys
    /// in the keys argument. The compositor must not send this event if the
    /// wl_keyboard already had an active surface immediately before this event.
    ///
    /// Event handler for Enter
    /// - [serial]: serial number of the enter event
    /// - [surface]: surface gaining keyboard focus
    /// - [keys]: the keys currently logically down
    void onEnter(KeyboardEnterEventHandler handler) {
      _enterHandler = handler;
    }
  
    KeyboardEnterEventHandler? _enterHandler;
  
    /// leave event
    ///
    /// Notification that this seat's keyboard focus is no longer on
    /// a certain surface.
    ///
    /// The leave notification is sent before the enter notification
    /// for the new focus.
    ///
    /// In the wl_keyboard logical state, this event resets all values to their
    /// defaults. The compositor must not send this event if the active surface
    /// of the wl_keyboard was not equal to the surface argument immediately
    /// before this event.
    ///
    /// Event handler for Leave
    /// - [serial]: serial number of the leave event
    /// - [surface]: surface that lost keyboard focus
    void onLeave(KeyboardLeaveEventHandler handler) {
      _leaveHandler = handler;
    }
  
    KeyboardLeaveEventHandler? _leaveHandler;
  
    /// key event
    ///
    /// A key was pressed or released.
    /// The time argument is a timestamp with millisecond
    /// granularity, with an undefined base.
    ///
    /// The key is a platform-specific key code that can be interpreted
    /// by feeding it to the keyboard mapping (see the keymap event).
    ///
    /// If this event produces a change in modifiers, then the resulting
    /// wl_keyboard.modifiers event must be sent after this event.
    ///
    /// In the wl_keyboard logical state, this event adds the key to the keys
    /// currently logically down (if the state argument is pressed) or removes
    /// the key from the keys currently logically down (if the state argument is
    /// released). The compositor must not send this event if the wl_keyboard
    /// did not have an active surface immediately before this event. The
    /// compositor must not send this event if state is pressed (resp. released)
    /// and the key was already logically down (resp. was not logically down)
    /// immediately before this event.
    ///
    /// Event handler for Key
    /// - [serial]: serial number of the key event
    /// - [time]: timestamp with millisecond granularity
    /// - [key]: key that produced the event
    /// - [state]: physical state of the key
    void onKey(KeyboardKeyEventHandler handler) {
      _keyHandler = handler;
    }
  
    KeyboardKeyEventHandler? _keyHandler;
  
    /// modifier and group state
    ///
    /// Notifies clients that the modifier and/or group state has
    /// changed, and it should update its local state.
    ///
    /// The compositor may send this event without a surface of the client
    /// having keyboard focus, for example to tie modifier information to
    /// pointer focus instead. If a modifier event with pressed modifiers is sent
    /// without a prior enter event, the client can assume the modifier state is
    /// valid until it receives the next wl_keyboard.modifiers event. In order to
    /// reset the modifier state again, the compositor can send a
    /// wl_keyboard.modifiers event with no pressed modifiers.
    ///
    /// In the wl_keyboard logical state, this event updates the modifiers and
    /// group.
    ///
    /// Event handler for Modifiers
    /// - [serial]: serial number of the modifiers event
    /// - [mods_depressed]: depressed modifiers
    /// - [mods_latched]: latched modifiers
    /// - [mods_locked]: locked modifiers
    /// - [group]: keyboard layout
    void onModifiers(KeyboardModifiersEventHandler handler) {
      _modifiersHandler = handler;
    }
  
    KeyboardModifiersEventHandler? _modifiersHandler;
  
    /// repeat rate and delay
    ///
    /// Informs the client about the keyboard's repeat rate and delay.
    ///
    /// This event is sent as soon as the wl_keyboard object has been created,
    /// and is guaranteed to be received by the client before any key press
    /// event.
    ///
    /// Negative values for either rate or delay are illegal. A rate of zero
    /// will disable any repeating (regardless of the value of delay).
    ///
    /// This event can be sent later on as well with a new value if necessary,
    /// so clients should continue listening for the event past the creation
    /// of wl_keyboard.
    ///
    /// Event handler for RepeatInfo
    /// - [rate]: the rate of repeating keys in characters per second
    /// - [delay]: delay in milliseconds since key down until repeating starts
    void onRepeatInfo(KeyboardRepeatInfoEventHandler handler) {
      _repeatInfoHandler = handler;
    }
  
    KeyboardRepeatInfoEventHandler? _repeatInfoHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Keyboard.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (fd != -1) {}
          if (_keymapHandler != null) {
            var offset = 0;
            final format =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final fd2 = fd;
            final size =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = KeyboardKeymapEvent(
              format,
              fd2,
              size,
            );
            _keymapHandler!(event);
          }
          break;
        case 1:
          if (_enterHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final surface = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            final keys = getArray(data, offset);
            var arrayLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4 + arrayLength;
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            var event = KeyboardEnterEvent(
              serial,
              surface,
              keys,
            );
            _enterHandler!(event);
          }
          break;
        case 2:
          if (_leaveHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final surface = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            var event = KeyboardLeaveEvent(
              serial,
              surface,
            );
            _leaveHandler!(event);
          }
          break;
        case 3:
          if (_keyHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final key =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final state =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = KeyboardKeyEvent(
              serial,
              time,
              key,
              state,
            );
            _keyHandler!(event);
          }
          break;
        case 4:
          if (_modifiersHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final modsDepressed =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final modsLatched =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final modsLocked =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final group =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            var event = KeyboardModifiersEvent(
              serial,
              modsDepressed,
              modsLatched,
              modsLocked,
              group,
            );
            _modifiersHandler!(event);
          }
          break;
        case 5:
          if (_repeatInfoHandler != null) {
            var offset = 0;
            final rate =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final delay =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = KeyboardRepeatInfoEvent(
              rate,
              delay,
            );
            _repeatInfoHandler!(event);
          }
          break;
      }
    }
  }
  
  /// keyboard mapping format
  ///
  /// This specifies the format of the keymap provided to the
  /// client with the wl_keyboard.keymap event.
  ///
  
  enum KeyboardKeymapFormat {
    /// no keymap; client must understand how to interpret the raw keycode
    noKeymap("no_keymap", 0),
  
    /// libxkbcommon compatible, null-terminated string; to determine the xkb keycode, clients must add 8 to the key event keycode
    xkbV1("xkb_v1", 1);
  
    const KeyboardKeymapFormat(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "KeyboardKeymapFormat {name: $enumName, value: $enumValue}";
    }
  }
  
  /// physical key state
  ///
  /// Describes the physical state of a key that produced the key event.
  ///
  
  enum KeyboardKeyState {
    /// key is not pressed
    released("released", 0),
  
    /// key is pressed
    pressed("pressed", 1);
  
    const KeyboardKeyState(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "KeyboardKeyState {name: $enumName, value: $enumValue}";
    }
  }
  
  /// touch down event and beginning of a touch sequence
  ///
  /// A new touch point has appeared on the surface. This touch point is
  /// assigned a unique ID. Future events from this touch point reference
  /// this ID. The ID ceases to be valid after a touch up event and may be
  /// reused in the future.
  ///
  class TouchDownEvent {
    /// serial number of the touch down event
    final int serial;
  
    /// timestamp with millisecond granularity
    final int time;
  
    /// surface touched
    final int surface;
  
    /// the unique ID of this touch point
    final int id;
  
    /// surface-local x coordinate
    final double x;
  
    /// surface-local y coordinate
    final double y;
  
    TouchDownEvent(
      this.serial,
      this.time,
      this.surface,
      this.id,
      this.x,
      this.y,
    );
  
    @override
    toString() {
      return "TouchDownEvent (serial: $serial, time: $time, surface: $surface, id: $id, x: $x, y: $y)";
    }
  }
  
  typedef TouchDownEventHandler = void Function(TouchDownEvent);
  
  /// end of a touch event sequence
  ///
  /// The touch point has disappeared. No further events will be sent for
  /// this touch point and the touch point's ID is released and may be
  /// reused in a future touch down event.
  ///
  class TouchUpEvent {
    /// serial number of the touch up event
    final int serial;
  
    /// timestamp with millisecond granularity
    final int time;
  
    /// the unique ID of this touch point
    final int id;
  
    TouchUpEvent(
      this.serial,
      this.time,
      this.id,
    );
  
    @override
    toString() {
      return "TouchUpEvent (serial: $serial, time: $time, id: $id)";
    }
  }
  
  typedef TouchUpEventHandler = void Function(TouchUpEvent);
  
  /// update of touch point coordinates
  ///
  /// A touch point has changed coordinates.
  ///
  class TouchMotionEvent {
    /// timestamp with millisecond granularity
    final int time;
  
    /// the unique ID of this touch point
    final int id;
  
    /// surface-local x coordinate
    final double x;
  
    /// surface-local y coordinate
    final double y;
  
    TouchMotionEvent(
      this.time,
      this.id,
      this.x,
      this.y,
    );
  
    @override
    toString() {
      return "TouchMotionEvent (time: $time, id: $id, x: $x, y: $y)";
    }
  }
  
  typedef TouchMotionEventHandler = void Function(TouchMotionEvent);
  
  /// end of touch frame event
  ///
  /// Indicates the end of a set of events that logically belong together.
  /// A client is expected to accumulate the data in all events within the
  /// frame before proceeding.
  ///
  /// A wl_touch.frame terminates at least one event but otherwise no
  /// guarantee is provided about the set of events within a frame. A client
  /// must assume that any state not updated in a frame is unchanged from the
  /// previously known state.
  ///
  class TouchFrameEvent {
    TouchFrameEvent();
  
    @override
    toString() {
      return "TouchFrameEvent ()";
    }
  }
  
  typedef TouchFrameEventHandler = void Function(TouchFrameEvent);
  
  /// touch session cancelled
  ///
  /// Sent if the compositor decides the touch stream is a global
  /// gesture. No further events are sent to the clients from that
  /// particular gesture. Touch cancellation applies to all touch points
  /// currently active on this client's surface. The client is
  /// responsible for finalizing the touch points, future touch points on
  /// this surface may reuse the touch point ID.
  ///
  /// No frame event is required after the cancel event.
  ///
  class TouchCancelEvent {
    TouchCancelEvent();
  
    @override
    toString() {
      return "TouchCancelEvent ()";
    }
  }
  
  typedef TouchCancelEventHandler = void Function(TouchCancelEvent);
  
  /// update shape of touch point
  ///
  /// Sent when a touchpoint has changed its shape.
  ///
  /// This event does not occur on its own. It is sent before a
  /// wl_touch.frame event and carries the new shape information for
  /// any previously reported, or new touch points of that frame.
  ///
  /// Other events describing the touch point such as wl_touch.down,
  /// wl_touch.motion or wl_touch.orientation may be sent within the
  /// same wl_touch.frame. A client should treat these events as a single
  /// logical touch point update. The order of wl_touch.shape,
  /// wl_touch.orientation and wl_touch.motion is not guaranteed.
  /// A wl_touch.down event is guaranteed to occur before the first
  /// wl_touch.shape event for this touch ID but both events may occur within
  /// the same wl_touch.frame.
  ///
  /// A touchpoint shape is approximated by an ellipse through the major and
  /// minor axis length. The major axis length describes the longer diameter
  /// of the ellipse, while the minor axis length describes the shorter
  /// diameter. Major and minor are orthogonal and both are specified in
  /// surface-local coordinates. The center of the ellipse is always at the
  /// touchpoint location as reported by wl_touch.down or wl_touch.move.
  ///
  /// This event is only sent by the compositor if the touch device supports
  /// shape reports. The client has to make reasonable assumptions about the
  /// shape if it did not receive this event.
  ///
  class TouchShapeEvent {
    /// the unique ID of this touch point
    final int id;
  
    /// length of the major axis in surface-local coordinates
    final double major;
  
    /// length of the minor axis in surface-local coordinates
    final double minor;
  
    TouchShapeEvent(
      this.id,
      this.major,
      this.minor,
    );
  
    @override
    toString() {
      return "TouchShapeEvent (id: $id, major: $major, minor: $minor)";
    }
  }
  
  typedef TouchShapeEventHandler = void Function(TouchShapeEvent);
  
  /// update orientation of touch point
  ///
  /// Sent when a touchpoint has changed its orientation.
  ///
  /// This event does not occur on its own. It is sent before a
  /// wl_touch.frame event and carries the new shape information for
  /// any previously reported, or new touch points of that frame.
  ///
  /// Other events describing the touch point such as wl_touch.down,
  /// wl_touch.motion or wl_touch.shape may be sent within the
  /// same wl_touch.frame. A client should treat these events as a single
  /// logical touch point update. The order of wl_touch.shape,
  /// wl_touch.orientation and wl_touch.motion is not guaranteed.
  /// A wl_touch.down event is guaranteed to occur before the first
  /// wl_touch.orientation event for this touch ID but both events may occur
  /// within the same wl_touch.frame.
  ///
  /// The orientation describes the clockwise angle of a touchpoint's major
  /// axis to the positive surface y-axis and is normalized to the -180 to
  /// +180 degree range. The granularity of orientation depends on the touch
  /// device, some devices only support binary rotation values between 0 and
  /// 90 degrees.
  ///
  /// This event is only sent by the compositor if the touch device supports
  /// orientation reports.
  ///
  class TouchOrientationEvent {
    /// the unique ID of this touch point
    final int id;
  
    /// angle between major axis and positive surface y-axis in degrees
    final double orientation;
  
    TouchOrientationEvent(
      this.id,
      this.orientation,
    );
  
    @override
    toString() {
      return "TouchOrientationEvent (id: $id, orientation: $orientation)";
    }
  }
  
  typedef TouchOrientationEventHandler = void Function(TouchOrientationEvent);
  
  /// touchscreen input device
  ///
  /// The wl_touch interface represents a touchscreen
  /// associated with a seat.
  ///
  /// Touch interactions can consist of one or more contacts.
  /// For each contact, a series of events is generated, starting
  /// with a down event, followed by zero or more motion events,
  /// and ending with an up event. Events relating to the same
  /// contact point can be identified by the ID of the sequence.
  ///
  class Touch extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 9;
  
    Touch(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Touch {name: 'wl_touch', id: '$objectId', version: '9',}";
    }
  
    /// release the touch object
    ///
    Result<void, Object> release() {
      innerContext.unRegister(this);
      logLn("Touch::release ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Touch::release: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// touch down event and beginning of a touch sequence
    ///
    /// A new touch point has appeared on the surface. This touch point is
    /// assigned a unique ID. Future events from this touch point reference
    /// this ID. The ID ceases to be valid after a touch up event and may be
    /// reused in the future.
    ///
    /// Event handler for Down
    /// - [serial]: serial number of the touch down event
    /// - [time]: timestamp with millisecond granularity
    /// - [surface]: surface touched
    /// - [id]: the unique ID of this touch point
    /// - [x]: surface-local x coordinate
    /// - [y]: surface-local y coordinate
    void onDown(TouchDownEventHandler handler) {
      _downHandler = handler;
    }
  
    TouchDownEventHandler? _downHandler;
  
    /// end of a touch event sequence
    ///
    /// The touch point has disappeared. No further events will be sent for
    /// this touch point and the touch point's ID is released and may be
    /// reused in a future touch down event.
    ///
    /// Event handler for Up
    /// - [serial]: serial number of the touch up event
    /// - [time]: timestamp with millisecond granularity
    /// - [id]: the unique ID of this touch point
    void onUp(TouchUpEventHandler handler) {
      _upHandler = handler;
    }
  
    TouchUpEventHandler? _upHandler;
  
    /// update of touch point coordinates
    ///
    /// A touch point has changed coordinates.
    ///
    /// Event handler for Motion
    /// - [time]: timestamp with millisecond granularity
    /// - [id]: the unique ID of this touch point
    /// - [x]: surface-local x coordinate
    /// - [y]: surface-local y coordinate
    void onMotion(TouchMotionEventHandler handler) {
      _motionHandler = handler;
    }
  
    TouchMotionEventHandler? _motionHandler;
  
    /// end of touch frame event
    ///
    /// Indicates the end of a set of events that logically belong together.
    /// A client is expected to accumulate the data in all events within the
    /// frame before proceeding.
    ///
    /// A wl_touch.frame terminates at least one event but otherwise no
    /// guarantee is provided about the set of events within a frame. A client
    /// must assume that any state not updated in a frame is unchanged from the
    /// previously known state.
    ///
    /// Event handler for Frame
    void onFrame(TouchFrameEventHandler handler) {
      _frameHandler = handler;
    }
  
    TouchFrameEventHandler? _frameHandler;
  
    /// touch session cancelled
    ///
    /// Sent if the compositor decides the touch stream is a global
    /// gesture. No further events are sent to the clients from that
    /// particular gesture. Touch cancellation applies to all touch points
    /// currently active on this client's surface. The client is
    /// responsible for finalizing the touch points, future touch points on
    /// this surface may reuse the touch point ID.
    ///
    /// No frame event is required after the cancel event.
    ///
    /// Event handler for Cancel
    void onCancel(TouchCancelEventHandler handler) {
      _cancelHandler = handler;
    }
  
    TouchCancelEventHandler? _cancelHandler;
  
    /// update shape of touch point
    ///
    /// Sent when a touchpoint has changed its shape.
    ///
    /// This event does not occur on its own. It is sent before a
    /// wl_touch.frame event and carries the new shape information for
    /// any previously reported, or new touch points of that frame.
    ///
    /// Other events describing the touch point such as wl_touch.down,
    /// wl_touch.motion or wl_touch.orientation may be sent within the
    /// same wl_touch.frame. A client should treat these events as a single
    /// logical touch point update. The order of wl_touch.shape,
    /// wl_touch.orientation and wl_touch.motion is not guaranteed.
    /// A wl_touch.down event is guaranteed to occur before the first
    /// wl_touch.shape event for this touch ID but both events may occur within
    /// the same wl_touch.frame.
    ///
    /// A touchpoint shape is approximated by an ellipse through the major and
    /// minor axis length. The major axis length describes the longer diameter
    /// of the ellipse, while the minor axis length describes the shorter
    /// diameter. Major and minor are orthogonal and both are specified in
    /// surface-local coordinates. The center of the ellipse is always at the
    /// touchpoint location as reported by wl_touch.down or wl_touch.move.
    ///
    /// This event is only sent by the compositor if the touch device supports
    /// shape reports. The client has to make reasonable assumptions about the
    /// shape if it did not receive this event.
    ///
    /// Event handler for Shape
    /// - [id]: the unique ID of this touch point
    /// - [major]: length of the major axis in surface-local coordinates
    /// - [minor]: length of the minor axis in surface-local coordinates
    void onShape(TouchShapeEventHandler handler) {
      _shapeHandler = handler;
    }
  
    TouchShapeEventHandler? _shapeHandler;
  
    /// update orientation of touch point
    ///
    /// Sent when a touchpoint has changed its orientation.
    ///
    /// This event does not occur on its own. It is sent before a
    /// wl_touch.frame event and carries the new shape information for
    /// any previously reported, or new touch points of that frame.
    ///
    /// Other events describing the touch point such as wl_touch.down,
    /// wl_touch.motion or wl_touch.shape may be sent within the
    /// same wl_touch.frame. A client should treat these events as a single
    /// logical touch point update. The order of wl_touch.shape,
    /// wl_touch.orientation and wl_touch.motion is not guaranteed.
    /// A wl_touch.down event is guaranteed to occur before the first
    /// wl_touch.orientation event for this touch ID but both events may occur
    /// within the same wl_touch.frame.
    ///
    /// The orientation describes the clockwise angle of a touchpoint's major
    /// axis to the positive surface y-axis and is normalized to the -180 to
    /// +180 degree range. The granularity of orientation depends on the touch
    /// device, some devices only support binary rotation values between 0 and
    /// 90 degrees.
    ///
    /// This event is only sent by the compositor if the touch device supports
    /// orientation reports.
    ///
    /// Event handler for Orientation
    /// - [id]: the unique ID of this touch point
    /// - [orientation]: angle between major axis and positive surface y-axis in degrees
    void onOrientation(TouchOrientationEventHandler handler) {
      _orientationHandler = handler;
    }
  
    TouchOrientationEventHandler? _orientationHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Touch.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_downHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final surface = innerContext
                .getProxy(
                    ByteData.view(data.buffer).getUint32(offset, Endian.little))
                .objectId;
            offset += 4;
            final id = ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final x = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final y = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = TouchDownEvent(
              serial,
              time,
              surface,
              id,
              x,
              y,
            );
            _downHandler!(event);
          }
          break;
        case 1:
          if (_upHandler != null) {
            var offset = 0;
            final serial =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final id = ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = TouchUpEvent(
              serial,
              time,
              id,
            );
            _upHandler!(event);
          }
          break;
        case 2:
          if (_motionHandler != null) {
            var offset = 0;
            final time =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final id = ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final x = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final y = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = TouchMotionEvent(
              time,
              id,
              x,
              y,
            );
            _motionHandler!(event);
          }
          break;
        case 3:
          if (_frameHandler != null) {
            _frameHandler!(TouchFrameEvent());
          }
          break;
        case 4:
          if (_cancelHandler != null) {
            _cancelHandler!(TouchCancelEvent());
          }
          break;
        case 5:
          if (_shapeHandler != null) {
            var offset = 0;
            final id = ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final major = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            final minor = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = TouchShapeEvent(
              id,
              major,
              minor,
            );
            _shapeHandler!(event);
          }
          break;
        case 6:
          if (_orientationHandler != null) {
            var offset = 0;
            final id = ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final orientation = fixedToDouble(
                ByteData.view(data.buffer).getInt32(offset, Endian.little));
            offset += 4;
            var event = TouchOrientationEvent(
              id,
              orientation,
            );
            _orientationHandler!(event);
          }
          break;
      }
    }
  }
  
  /// properties of the output
  ///
  /// The geometry event describes geometric properties of the output.
  /// The event is sent when binding to the output object and whenever
  /// any of the properties change.
  ///
  /// The physical size can be set to zero if it doesn't make sense for this
  /// output (e.g. for projectors or virtual outputs).
  ///
  /// The geometry event will be followed by a done event (starting from
  /// version 2).
  ///
  /// Clients should use wl_surface.preferred_buffer_transform instead of the
  /// transform advertised by this event to find the preferred buffer
  /// transform to use for a surface.
  ///
  /// Note: wl_output only advertises partial information about the output
  /// position and identification. Some compositors, for instance those not
  /// implementing a desktop-style output layout or those exposing virtual
  /// outputs, might fake this information. Instead of using x and y, clients
  /// should use xdg_output.logical_position. Instead of using make and model,
  /// clients should use name and description.
  ///
  class OutputGeometryEvent {
    /// x position within the global compositor space
    final int x;
  
    /// y position within the global compositor space
    final int y;
  
    /// width in millimeters of the output
    final int physicalWidth;
  
    /// height in millimeters of the output
    final int physicalHeight;
  
    /// subpixel orientation of the output
    final int subpixel;
  
    /// textual description of the manufacturer
    final String make;
  
    /// textual description of the model
    final String model;
  
    /// additional transformation applied to buffer contents during presentation
    final int transform;
  
    OutputGeometryEvent(
      this.x,
      this.y,
      this.physicalWidth,
      this.physicalHeight,
      this.subpixel,
      this.make,
      this.model,
      this.transform,
    );
  
    @override
    toString() {
      return "OutputGeometryEvent (x: $x, y: $y, physicalWidth: $physicalWidth, ..., model: $model, transform: $transform)";
    }
  }
  
  typedef OutputGeometryEventHandler = void Function(OutputGeometryEvent);
  
  /// advertise available modes for the output
  ///
  /// The mode event describes an available mode for the output.
  ///
  /// The event is sent when binding to the output object and there
  /// will always be one mode, the current mode.  The event is sent
  /// again if an output changes mode, for the mode that is now
  /// current.  In other words, the current mode is always the last
  /// mode that was received with the current flag set.
  ///
  /// Non-current modes are deprecated. A compositor can decide to only
  /// advertise the current mode and never send other modes. Clients
  /// should not rely on non-current modes.
  ///
  /// The size of a mode is given in physical hardware units of
  /// the output device. This is not necessarily the same as
  /// the output size in the global compositor space. For instance,
  /// the output may be scaled, as described in wl_output.scale,
  /// or transformed, as described in wl_output.transform. Clients
  /// willing to retrieve the output size in the global compositor
  /// space should use xdg_output.logical_size instead.
  ///
  /// The vertical refresh rate can be set to zero if it doesn't make
  /// sense for this output (e.g. for virtual outputs).
  ///
  /// The mode event will be followed by a done event (starting from
  /// version 2).
  ///
  /// Clients should not use the refresh rate to schedule frames. Instead,
  /// they should use the wl_surface.frame event or the presentation-time
  /// protocol.
  ///
  /// Note: this information is not always meaningful for all outputs. Some
  /// compositors, such as those exposing virtual outputs, might fake the
  /// refresh rate or the size.
  ///
  class OutputModeEvent {
    /// bitfield of mode flags
    final int flags;
  
    /// width of the mode in hardware units
    final int width;
  
    /// height of the mode in hardware units
    final int height;
  
    /// vertical refresh rate in mHz
    final int refresh;
  
    OutputModeEvent(
      this.flags,
      this.width,
      this.height,
      this.refresh,
    );
  
    @override
    toString() {
      return "OutputModeEvent (flags: $flags, width: $width, height: $height, refresh: $refresh)";
    }
  }
  
  typedef OutputModeEventHandler = void Function(OutputModeEvent);
  
  /// sent all information about output
  ///
  /// This event is sent after all other properties have been
  /// sent after binding to the output object and after any
  /// other property changes done after that. This allows
  /// changes to the output properties to be seen as
  /// atomic, even if they happen via multiple events.
  ///
  class OutputDoneEvent {
    OutputDoneEvent();
  
    @override
    toString() {
      return "OutputDoneEvent ()";
    }
  }
  
  typedef OutputDoneEventHandler = void Function(OutputDoneEvent);
  
  /// output scaling properties
  ///
  /// This event contains scaling geometry information
  /// that is not in the geometry event. It may be sent after
  /// binding the output object or if the output scale changes
  /// later. The compositor will emit a non-zero, positive
  /// value for scale. If it is not sent, the client should
  /// assume a scale of 1.
  ///
  /// A scale larger than 1 means that the compositor will
  /// automatically scale surface buffers by this amount
  /// when rendering. This is used for very high resolution
  /// displays where applications rendering at the native
  /// resolution would be too small to be legible.
  ///
  /// Clients should use wl_surface.preferred_buffer_scale
  /// instead of this event to find the preferred buffer
  /// scale to use for a surface.
  ///
  /// The scale event will be followed by a done event.
  ///
  class OutputScaleEvent {
    /// scaling factor of output
    final int factor;
  
    OutputScaleEvent(
      this.factor,
    );
  
    @override
    toString() {
      return "OutputScaleEvent (factor: $factor)";
    }
  }
  
  typedef OutputScaleEventHandler = void Function(OutputScaleEvent);
  
  /// name of this output
  ///
  /// Many compositors will assign user-friendly names to their outputs, show
  /// them to the user, allow the user to refer to an output, etc. The client
  /// may wish to know this name as well to offer the user similar behaviors.
  ///
  /// The name is a UTF-8 string with no convention defined for its contents.
  /// Each name is unique among all wl_output globals. The name is only
  /// guaranteed to be unique for the compositor instance.
  ///
  /// The same output name is used for all clients for a given wl_output
  /// global. Thus, the name can be shared across processes to refer to a
  /// specific wl_output global.
  ///
  /// The name is not guaranteed to be persistent across sessions, thus cannot
  /// be used to reliably identify an output in e.g. configuration files.
  ///
  /// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
  /// not assume that the name is a reflection of an underlying DRM connector,
  /// X11 connection, etc.
  ///
  /// The name event is sent after binding the output object. This event is
  /// only sent once per output object, and the name does not change over the
  /// lifetime of the wl_output global.
  ///
  /// Compositors may re-use the same output name if the wl_output global is
  /// destroyed and re-created later. Compositors should avoid re-using the
  /// same name if possible.
  ///
  /// The name event will be followed by a done event.
  ///
  class OutputNameEvent {
    /// output name
    final String name;
  
    OutputNameEvent(
      this.name,
    );
  
    @override
    toString() {
      return "OutputNameEvent (name: $name)";
    }
  }
  
  typedef OutputNameEventHandler = void Function(OutputNameEvent);
  
  /// human-readable description of this output
  ///
  /// Many compositors can produce human-readable descriptions of their
  /// outputs. The client may wish to know this description as well, e.g. for
  /// output selection purposes.
  ///
  /// The description is a UTF-8 string with no convention defined for its
  /// contents. The description is not guaranteed to be unique among all
  /// wl_output globals. Examples might include 'Foocorp 11" Display' or
  /// 'Virtual X11 output via :1'.
  ///
  /// The description event is sent after binding the output object and
  /// whenever the description changes. The description is optional, and may
  /// not be sent at all.
  ///
  /// The description event will be followed by a done event.
  ///
  class OutputDescriptionEvent {
    /// output description
    final String description;
  
    OutputDescriptionEvent(
      this.description,
    );
  
    @override
    toString() {
      return "OutputDescriptionEvent (description: $description)";
    }
  }
  
  typedef OutputDescriptionEventHandler = void Function(OutputDescriptionEvent);
  
  /// compositor output region
  ///
  /// An output describes part of the compositor geometry.  The
  /// compositor works in the 'compositor coordinate system' and an
  /// output corresponds to a rectangular area in that space that is
  /// actually visible.  This typically corresponds to a monitor that
  /// displays part of the compositor space.  This object is published
  /// as global during start up, or when a monitor is hotplugged.
  ///
  class Output extends Proxy implements Dispatcher {
    final Context innerContext;
    final version = 4;
  
    Output(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Output {name: 'wl_output', id: '$objectId', version: '4',}";
    }
  
    /// release the output object
    ///
    /// Using this request a client can tell the server that it is not going to
    /// use the output object anymore.
    ///
    Result<void, Object> release() {
      innerContext.unRegister(this);
      logLn("Output::release ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Output::release: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// properties of the output
    ///
    /// The geometry event describes geometric properties of the output.
    /// The event is sent when binding to the output object and whenever
    /// any of the properties change.
    ///
    /// The physical size can be set to zero if it doesn't make sense for this
    /// output (e.g. for projectors or virtual outputs).
    ///
    /// The geometry event will be followed by a done event (starting from
    /// version 2).
    ///
    /// Clients should use wl_surface.preferred_buffer_transform instead of the
    /// transform advertised by this event to find the preferred buffer
    /// transform to use for a surface.
    ///
    /// Note: wl_output only advertises partial information about the output
    /// position and identification. Some compositors, for instance those not
    /// implementing a desktop-style output layout or those exposing virtual
    /// outputs, might fake this information. Instead of using x and y, clients
    /// should use xdg_output.logical_position. Instead of using make and model,
    /// clients should use name and description.
    ///
    /// Event handler for Geometry
    /// - [x]: x position within the global compositor space
    /// - [y]: y position within the global compositor space
    /// - [physical_width]: width in millimeters of the output
    /// - [physical_height]: height in millimeters of the output
    /// - [subpixel]: subpixel orientation of the output
    /// - [make]: textual description of the manufacturer
    /// - [model]: textual description of the model
    /// - [transform]: additional transformation applied to buffer contents during presentation
    void onGeometry(OutputGeometryEventHandler handler) {
      _geometryHandler = handler;
    }
  
    OutputGeometryEventHandler? _geometryHandler;
  
    /// advertise available modes for the output
    ///
    /// The mode event describes an available mode for the output.
    ///
    /// The event is sent when binding to the output object and there
    /// will always be one mode, the current mode.  The event is sent
    /// again if an output changes mode, for the mode that is now
    /// current.  In other words, the current mode is always the last
    /// mode that was received with the current flag set.
    ///
    /// Non-current modes are deprecated. A compositor can decide to only
    /// advertise the current mode and never send other modes. Clients
    /// should not rely on non-current modes.
    ///
    /// The size of a mode is given in physical hardware units of
    /// the output device. This is not necessarily the same as
    /// the output size in the global compositor space. For instance,
    /// the output may be scaled, as described in wl_output.scale,
    /// or transformed, as described in wl_output.transform. Clients
    /// willing to retrieve the output size in the global compositor
    /// space should use xdg_output.logical_size instead.
    ///
    /// The vertical refresh rate can be set to zero if it doesn't make
    /// sense for this output (e.g. for virtual outputs).
    ///
    /// The mode event will be followed by a done event (starting from
    /// version 2).
    ///
    /// Clients should not use the refresh rate to schedule frames. Instead,
    /// they should use the wl_surface.frame event or the presentation-time
    /// protocol.
    ///
    /// Note: this information is not always meaningful for all outputs. Some
    /// compositors, such as those exposing virtual outputs, might fake the
    /// refresh rate or the size.
    ///
    /// Event handler for Mode
    /// - [flags]: bitfield of mode flags
    /// - [width]: width of the mode in hardware units
    /// - [height]: height of the mode in hardware units
    /// - [refresh]: vertical refresh rate in mHz
    void onMode(OutputModeEventHandler handler) {
      _modeHandler = handler;
    }
  
    OutputModeEventHandler? _modeHandler;
  
    /// sent all information about output
    ///
    /// This event is sent after all other properties have been
    /// sent after binding to the output object and after any
    /// other property changes done after that. This allows
    /// changes to the output properties to be seen as
    /// atomic, even if they happen via multiple events.
    ///
    /// Event handler for Done
    void onDone(OutputDoneEventHandler handler) {
      _doneHandler = handler;
    }
  
    OutputDoneEventHandler? _doneHandler;
  
    /// output scaling properties
    ///
    /// This event contains scaling geometry information
    /// that is not in the geometry event. It may be sent after
    /// binding the output object or if the output scale changes
    /// later. The compositor will emit a non-zero, positive
    /// value for scale. If it is not sent, the client should
    /// assume a scale of 1.
    ///
    /// A scale larger than 1 means that the compositor will
    /// automatically scale surface buffers by this amount
    /// when rendering. This is used for very high resolution
    /// displays where applications rendering at the native
    /// resolution would be too small to be legible.
    ///
    /// Clients should use wl_surface.preferred_buffer_scale
    /// instead of this event to find the preferred buffer
    /// scale to use for a surface.
    ///
    /// The scale event will be followed by a done event.
    ///
    /// Event handler for Scale
    /// - [factor]: scaling factor of output
    void onScale(OutputScaleEventHandler handler) {
      _scaleHandler = handler;
    }
  
    OutputScaleEventHandler? _scaleHandler;
  
    /// name of this output
    ///
    /// Many compositors will assign user-friendly names to their outputs, show
    /// them to the user, allow the user to refer to an output, etc. The client
    /// may wish to know this name as well to offer the user similar behaviors.
    ///
    /// The name is a UTF-8 string with no convention defined for its contents.
    /// Each name is unique among all wl_output globals. The name is only
    /// guaranteed to be unique for the compositor instance.
    ///
    /// The same output name is used for all clients for a given wl_output
    /// global. Thus, the name can be shared across processes to refer to a
    /// specific wl_output global.
    ///
    /// The name is not guaranteed to be persistent across sessions, thus cannot
    /// be used to reliably identify an output in e.g. configuration files.
    ///
    /// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
    /// not assume that the name is a reflection of an underlying DRM connector,
    /// X11 connection, etc.
    ///
    /// The name event is sent after binding the output object. This event is
    /// only sent once per output object, and the name does not change over the
    /// lifetime of the wl_output global.
    ///
    /// Compositors may re-use the same output name if the wl_output global is
    /// destroyed and re-created later. Compositors should avoid re-using the
    /// same name if possible.
    ///
    /// The name event will be followed by a done event.
    ///
    /// Event handler for Name
    /// - [name]: output name
    void onName(OutputNameEventHandler handler) {
      _nameHandler = handler;
    }
  
    OutputNameEventHandler? _nameHandler;
  
    /// human-readable description of this output
    ///
    /// Many compositors can produce human-readable descriptions of their
    /// outputs. The client may wish to know this description as well, e.g. for
    /// output selection purposes.
    ///
    /// The description is a UTF-8 string with no convention defined for its
    /// contents. The description is not guaranteed to be unique among all
    /// wl_output globals. Examples might include 'Foocorp 11" Display' or
    /// 'Virtual X11 output via :1'.
    ///
    /// The description event is sent after binding the output object and
    /// whenever the description changes. The description is optional, and may
    /// not be sent at all.
    ///
    /// The description event will be followed by a done event.
    ///
    /// Event handler for Description
    /// - [description]: output description
    void onDescription(OutputDescriptionEventHandler handler) {
      _descriptionHandler = handler;
    }
  
    OutputDescriptionEventHandler? _descriptionHandler;
  
    @override
    void dispatch(int opcode, int fd, Uint8List data) {
      logLn("Output.dispatch($opcode, $fd, $data)");
      switch (opcode) {
        case 0:
          if (_geometryHandler != null) {
            var offset = 0;
            final x = ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final y = ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final physicalWidth =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final physicalHeight =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final subpixel =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final makeLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final make =
                utf8.decode(data.sublist(offset, offset + makeLength - 1));
            offset += makeLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            final modelLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final model =
                utf8.decode(data.sublist(offset, offset + modelLength - 1));
            offset += modelLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            final transform =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = OutputGeometryEvent(
              x,
              y,
              physicalWidth,
              physicalHeight,
              subpixel,
              make,
              model,
              transform,
            );
            _geometryHandler!(event);
          }
          break;
        case 1:
          if (_modeHandler != null) {
            var offset = 0;
            final flags =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final width =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final height =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            final refresh =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = OutputModeEvent(
              flags,
              width,
              height,
              refresh,
            );
            _modeHandler!(event);
          }
          break;
        case 2:
          if (_doneHandler != null) {
            _doneHandler!(OutputDoneEvent());
          }
          break;
        case 3:
          if (_scaleHandler != null) {
            var offset = 0;
            final factor =
                ByteData.view(data.buffer).getInt32(offset, Endian.little);
            offset += 4;
            var event = OutputScaleEvent(
              factor,
            );
            _scaleHandler!(event);
          }
          break;
        case 4:
          if (_nameHandler != null) {
            var offset = 0;
            final nameLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final name =
                utf8.decode(data.sublist(offset, offset + nameLength - 1));
            offset += nameLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            var event = OutputNameEvent(
              name,
            );
            _nameHandler!(event);
          }
          break;
        case 5:
          if (_descriptionHandler != null) {
            var offset = 0;
            final descriptionLength =
                ByteData.view(data.buffer).getUint32(offset, Endian.little);
            offset += 4;
            final description =
                utf8.decode(data.sublist(offset, offset + descriptionLength - 1));
            offset +=
                descriptionLength; // Skip the string bytes and null terminator
            while (offset % 4 != 0) {
              offset++;
            } // Padding
            var event = OutputDescriptionEvent(
              description,
            );
            _descriptionHandler!(event);
          }
          break;
      }
    }
  }
  
  /// subpixel geometry information
  ///
  /// This enumeration describes how the physical
  /// pixels on an output are laid out.
  ///
  
  enum OutputSubpixel {
    /// unknown geometry
    unknown("unknown", 0),
  
    /// no geometry
    none("none", 1),
  
    /// horizontal RGB
    horizontalRgb("horizontal_rgb", 2),
  
    /// horizontal BGR
    horizontalBgr("horizontal_bgr", 3),
  
    /// vertical RGB
    verticalRgb("vertical_rgb", 4),
  
    /// vertical BGR
    verticalBgr("vertical_bgr", 5);
  
    const OutputSubpixel(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "OutputSubpixel {name: $enumName, value: $enumValue}";
    }
  }
  
  /// transformation applied to buffer contents
  ///
  /// This describes transformations that clients and compositors apply to
  /// buffer contents.
  ///
  /// The flipped values correspond to an initial flip around a
  /// vertical axis followed by rotation.
  ///
  /// The purpose is mainly to allow clients to render accordingly and
  /// tell the compositor, so that for fullscreen surfaces, the
  /// compositor will still be able to scan out directly from client
  /// surfaces.
  ///
  
  enum OutputTransform {
    /// no transform
    normal("normal", 0),
  
    /// 90 degrees counter-clockwise
    w90("90", 1),
  
    /// 180 degrees counter-clockwise
    w180("180", 2),
  
    /// 270 degrees counter-clockwise
    w270("270", 3),
  
    /// 180 degree flip around a vertical axis
    flipped("flipped", 4),
  
    /// flip and rotate 90 degrees counter-clockwise
    flipped90("flipped_90", 5),
  
    /// flip and rotate 180 degrees counter-clockwise
    flipped180("flipped_180", 6),
  
    /// flip and rotate 270 degrees counter-clockwise
    flipped270("flipped_270", 7);
  
    const OutputTransform(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "OutputTransform {name: $enumName, value: $enumValue}";
    }
  }
  
  /// mode information
  ///
  /// These flags describe properties of an output mode.
  /// They are used in the flags bitfield of the mode event.
  ///
  
  enum OutputMode {
    /// indicates this is the current mode
    current("current", 0x1),
  
    /// indicates this is the preferred mode
    preferred("preferred", 0x2);
  
    const OutputMode(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "OutputMode {name: $enumName, value: $enumValue}";
    }
  }
  
  /// region interface
  ///
  /// A region object describes an area.
  ///
  /// Region objects are used to describe the opaque and input
  /// regions of a surface.
  ///
  class Region extends Proxy {
    final Context innerContext;
    final version = 1;
  
    Region(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Region {name: 'wl_region', id: '$objectId', version: '1',}";
    }
  
    /// destroy region
    ///
    /// Destroy the region.  This will invalidate the object ID.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("Region::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Region::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// add rectangle to region
    ///
    /// Add the specified rectangle to the region.
    ///
    /// [x]: region-local x coordinate
    /// [y]: region-local y coordinate
    /// [width]: rectangle width
    /// [height]: rectangle height
    Result<void, Object> add(int x, int y, int width, int height) {
      logLn("Region::add  x: $x y: $y width: $width height: $height");
      var arguments = [x, y, width, height];
      var argTypes = <WaylandType>[
        WaylandType.int,
        WaylandType.int,
        WaylandType.int,
        WaylandType.int
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([width]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([height]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Region::add: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// subtract rectangle from region
    ///
    /// Subtract the specified rectangle from the region.
    ///
    /// [x]: region-local x coordinate
    /// [y]: region-local y coordinate
    /// [width]: rectangle width
    /// [height]: rectangle height
    Result<void, Object> subtract(int x, int y, int width, int height) {
      logLn("Region::subtract  x: $x y: $y width: $width height: $height");
      var arguments = [x, y, width, height];
      var argTypes = <WaylandType>[
        WaylandType.int,
        WaylandType.int,
        WaylandType.int,
        WaylandType.int
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([width]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([height]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Region::subtract: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  }
  
  /// sub-surface compositing
  ///
  /// The global interface exposing sub-surface compositing capabilities.
  /// A wl_surface, that has sub-surfaces associated, is called the
  /// parent surface. Sub-surfaces can be arbitrarily nested and create
  /// a tree of sub-surfaces.
  ///
  /// The root surface in a tree of sub-surfaces is the main
  /// surface. The main surface cannot be a sub-surface, because
  /// sub-surfaces must always have a parent.
  ///
  /// A main surface with its sub-surfaces forms a (compound) window.
  /// For window management purposes, this set of wl_surface objects is
  /// to be considered as a single window, and it should also behave as
  /// such.
  ///
  /// The aim of sub-surfaces is to offload some of the compositing work
  /// within a window from clients to the compositor. A prime example is
  /// a video player with decorations and video in separate wl_surface
  /// objects. This should allow the compositor to pass YUV video buffer
  /// processing to dedicated overlay hardware when possible.
  ///
  class Subcompositor extends Proxy {
    final Context innerContext;
    final version = 1;
  
    Subcompositor(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Subcompositor {name: 'wl_subcompositor', id: '$objectId', version: '1',}";
    }
  
    /// unbind from the subcompositor interface
    ///
    /// Informs the server that the client will not be using this
    /// protocol object anymore. This does not affect any other
    /// objects, wl_subsurface objects included.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("Subcompositor::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subcompositor::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// give a surface the role sub-surface
    ///
    /// Create a sub-surface interface for the given surface, and
    /// associate it with the given parent surface. This turns a
    /// plain wl_surface into a sub-surface.
    ///
    /// The to-be sub-surface must not already have another role, and it
    /// must not have an existing wl_subsurface object. Otherwise the
    /// bad_surface protocol error is raised.
    ///
    /// Adding sub-surfaces to a parent is a double-buffered operation on the
    /// parent (see wl_surface.commit). The effect of adding a sub-surface
    /// becomes visible on the next time the state of the parent surface is
    /// applied.
    ///
    /// The parent surface must not be one of the child surface's descendants,
    /// and the parent must be different from the child surface, otherwise the
    /// bad_parent protocol error is raised.
    ///
    /// This request modifies the behaviour of wl_surface.commit request on
    /// the sub-surface, see the documentation on wl_subsurface interface.
    ///
    /// [id]: the new sub-surface object ID
    /// [surface]: the surface to be turned into a sub-surface
    /// [parent]: the parent surface
    Result<Subsurface, Object> getSubsurface(Surface surface, Surface parent) {
      var id = Subsurface(innerContext);
      logLn(
          "Subcompositor::getSubsurface  id: $id surface: $surface parent: $parent");
      var arguments = [id, surface, parent];
      var argTypes = <WaylandType>[
        WaylandType.newId,
        WaylandType.object,
        WaylandType.object
      ];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder.add(Uint32List.fromList([id.objectId]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([surface.objectId]).buffer.asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([parent.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subcompositor::getSubsurface: $e");
        return Failure(e);
      }
      return Success(id);
    }
  }
  
  ///
  ///
  
  enum SubcompositorError {
    /// the to-be sub-surface is invalid
    badSurface("bad_surface", 0),
  
    /// the to-be sub-surface parent is invalid
    badParent("bad_parent", 1);
  
    const SubcompositorError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "SubcompositorError {name: $enumName, value: $enumValue}";
    }
  }
  
  /// sub-surface interface to a wl_surface
  ///
  /// An additional interface to a wl_surface object, which has been
  /// made a sub-surface. A sub-surface has one parent surface. A
  /// sub-surface's size and position are not limited to that of the parent.
  /// Particularly, a sub-surface is not automatically clipped to its
  /// parent's area.
  ///
  /// A sub-surface becomes mapped, when a non-NULL wl_buffer is applied
  /// and the parent surface is mapped. The order of which one happens
  /// first is irrelevant. A sub-surface is hidden if the parent becomes
  /// hidden, or if a NULL wl_buffer is applied. These rules apply
  /// recursively through the tree of surfaces.
  ///
  /// The behaviour of a wl_surface.commit request on a sub-surface
  /// depends on the sub-surface's mode. The possible modes are
  /// synchronized and desynchronized, see methods
  /// wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized
  /// mode caches the wl_surface state to be applied when the parent's
  /// state gets applied, and desynchronized mode applies the pending
  /// wl_surface state directly. A sub-surface is initially in the
  /// synchronized mode.
  ///
  /// Sub-surfaces also have another kind of state, which is managed by
  /// wl_subsurface requests, as opposed to wl_surface requests. This
  /// state includes the sub-surface position relative to the parent
  /// surface (wl_subsurface.set_position), and the stacking order of
  /// the parent and its sub-surfaces (wl_subsurface.place_above and
  /// .place_below). This state is applied when the parent surface's
  /// wl_surface state is applied, regardless of the sub-surface's mode.
  /// As the exception, set_sync and set_desync are effective immediately.
  ///
  /// The main surface can be thought to be always in desynchronized mode,
  /// since it does not have a parent in the sub-surfaces sense.
  ///
  /// Even if a sub-surface is in desynchronized mode, it will behave as
  /// in synchronized mode, if its parent surface behaves as in
  /// synchronized mode. This rule is applied recursively throughout the
  /// tree of surfaces. This means, that one can set a sub-surface into
  /// synchronized mode, and then assume that all its child and grand-child
  /// sub-surfaces are synchronized, too, without explicitly setting them.
  ///
  /// Destroying a sub-surface takes effect immediately. If you need to
  /// synchronize the removal of a sub-surface to the parent surface update,
  /// unmap the sub-surface first by attaching a NULL wl_buffer, update parent,
  /// and then destroy the sub-surface.
  ///
  /// If the parent wl_surface object is destroyed, the sub-surface is
  /// unmapped.
  ///
  /// A sub-surface never has the keyboard focus of any seat.
  ///
  /// The wl_surface.offset request is ignored: clients must use set_position
  /// instead to move the sub-surface.
  ///
  class Subsurface extends Proxy {
    final Context innerContext;
    final version = 1;
  
    Subsurface(this.innerContext) : super(innerContext.allocateClientId()) {
      innerContext.register(this);
    }
  
    @override
    toString() {
      return "Subsurface {name: 'wl_subsurface', id: '$objectId', version: '1',}";
    }
  
    /// remove sub-surface interface
    ///
    /// The sub-surface interface is removed from the wl_surface object
    /// that was turned into a sub-surface with a
    /// wl_subcompositor.get_subsurface request. The wl_surface's association
    /// to the parent is deleted. The wl_surface is unmapped immediately.
    ///
    Result<void, Object> destroy() {
      innerContext.unRegister(this);
      logLn("Subsurface::destroy ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 0])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subsurface::destroy: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// reposition the sub-surface
    ///
    /// This schedules a sub-surface position change.
    /// The sub-surface will be moved so that its origin (top left
    /// corner pixel) will be at the location x, y of the parent surface
    /// coordinate system. The coordinates are not restricted to the parent
    /// surface area. Negative values are allowed.
    ///
    /// The scheduled coordinates will take effect whenever the state of the
    /// parent surface is applied.
    ///
    /// If more than one set_position request is invoked by the client before
    /// the commit of the parent surface, the position of a new request always
    /// replaces the scheduled position from any previous request.
    ///
    /// The initial position is 0, 0.
    ///
    /// [x]: x coordinate in the parent surface
    /// [y]: y coordinate in the parent surface
    Result<void, Object> setPosition(int x, int y) {
      logLn("Subsurface::setPosition  x: $x y: $y");
      var arguments = [x, y];
      var argTypes = <WaylandType>[WaylandType.int, WaylandType.int];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 1])
              .buffer
              .asUint8List());
      bytesBuilder.add(Int32List.fromList([x]).buffer.asUint8List());
      bytesBuilder.add(Int32List.fromList([y]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subsurface::setPosition: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// restack the sub-surface
    ///
    /// This sub-surface is taken from the stack, and put back just
    /// above the reference surface, changing the z-order of the sub-surfaces.
    /// The reference surface must be one of the sibling surfaces, or the
    /// parent surface. Using any other surface, including this sub-surface,
    /// will cause a protocol error.
    ///
    /// The z-order is double-buffered. Requests are handled in order and
    /// applied immediately to a pending state. The final pending state is
    /// copied to the active state the next time the state of the parent
    /// surface is applied.
    ///
    /// A new sub-surface is initially added as the top-most in the stack
    /// of its siblings and parent.
    ///
    /// [sibling]: the reference surface
    Result<void, Object> placeAbove(Surface sibling) {
      logLn("Subsurface::placeAbove  sibling: $sibling");
      var arguments = [sibling];
      var argTypes = <WaylandType>[WaylandType.object];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 2])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([sibling.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subsurface::placeAbove: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// restack the sub-surface
    ///
    /// The sub-surface is placed just below the reference surface.
    /// See wl_subsurface.place_above.
    ///
    /// [sibling]: the reference surface
    Result<void, Object> placeBelow(Surface sibling) {
      logLn("Subsurface::placeBelow  sibling: $sibling");
      var arguments = [sibling];
      var argTypes = <WaylandType>[WaylandType.object];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 3])
              .buffer
              .asUint8List());
      bytesBuilder
          .add(Uint32List.fromList([sibling.objectId]).buffer.asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subsurface::placeBelow: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set sub-surface to synchronized mode
    ///
    /// Change the commit behaviour of the sub-surface to synchronized
    /// mode, also described as the parent dependent mode.
    ///
    /// In synchronized mode, wl_surface.commit on a sub-surface will
    /// accumulate the committed state in a cache, but the state will
    /// not be applied and hence will not change the compositor output.
    /// The cached state is applied to the sub-surface immediately after
    /// the parent surface's state is applied. This ensures atomic
    /// updates of the parent and all its synchronized sub-surfaces.
    /// Applying the cached state will invalidate the cache, so further
    /// parent surface commits do not (re-)apply old state.
    ///
    /// See wl_subsurface for the recursive effect of this mode.
    ///
    Result<void, Object> setSync() {
      logLn("Subsurface::setSync ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 4])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subsurface::setSync: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  
    /// set sub-surface to desynchronized mode
    ///
    /// Change the commit behaviour of the sub-surface to desynchronized
    /// mode, also described as independent or freely running mode.
    ///
    /// In desynchronized mode, wl_surface.commit on a sub-surface will
    /// apply the pending state directly, without caching, as happens
    /// normally with a wl_surface. Calling wl_surface.commit on the
    /// parent surface has no effect on the sub-surface's wl_surface
    /// state. This mode allows a sub-surface to be updated on its own.
    ///
    /// If cached state exists when wl_surface.commit is called in
    /// desynchronized mode, the pending state is added to the cached
    /// state, and applied as a whole. This invalidates the cache.
    ///
    /// Note: even if a sub-surface is set to desynchronized, a parent
    /// sub-surface may override it to behave as synchronized. For details,
    /// see wl_subsurface.
    ///
    /// If a surface's parent surface behaves as desynchronized, then
    /// the cached state is applied on set_desync.
    ///
    Result<void, Object> setDesync() {
      logLn("Subsurface::setDesync ");
      var arguments = [];
      var argTypes = <WaylandType>[];
      var calclulatedSize = calculateSize(argTypes, arguments);
      final bytesBuilder = BytesBuilder();
      bytesBuilder.add(
          Uint32List.fromList([objectId, (calclulatedSize << 16) | 5])
              .buffer
              .asUint8List());
      try {
        innerContext.sendMessage(
          bytesBuilder.toBytes(),
        );
      } catch (e) {
        logLn("Exception in Subsurface::setDesync: $e");
        return Failure(e);
      }
      return Success(Object());
    }
  }
  
  ///
  ///
  
  enum SubsurfaceError {
    /// wl_surface is not a sibling or the parent
    badSurface("bad_surface", 0);
  
    const SubsurfaceError(this.enumName, this.enumValue);
    final int enumValue;
    final String enumName;
    @override
    toString() {
      return "SubsurfaceError {name: $enumName, value: $enumValue}";
    }
  }
  