The eager-unmap logic (0c6c366) tracked mapping references per buffer but
not per surface. When a surface attached a virtwl buffer, it stored Cstruct
views into the mapping's bigarrays. If the buffer (and pool) were then
destroyed, the mapping was unmapped while the surface still held dangling
Cstruct references. The next commit would Cstruct.blit through the freed
bigarray data pointer (NULL post-unmap), segfaulting in libc memcpy.
Fix: the surface now participates in mapping ref counting. On attach it
increments the mapping ref via a closure on the virtwl_buffer; on re-attach,
null-attach, or surface destroy it decrements the old one. This keeps the
mapping alive as long as any surface references its memory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1989 lines
82 KiB
OCaml
1989 lines
82 KiB
OCaml
(* Relay Wayland messages between a client and a virtio_gpu host compositor.
|
|
When sending a file descriptor, we create a virtio_gpu descriptor of the appropriate type and send that instead.
|
|
For streams, we copy the data.
|
|
For buffers we copy the contents when the surface is committed (todo: copy just the damaged region).
|
|
We generally ignore the version part of the ocaml-wayland types and just cast as necessary.
|
|
Since we're relaying, we know that both sides are using the same version, so if we get e.g. a
|
|
version 5 request from the client then we know it's safe to send it to the host. *)
|
|
|
|
open Eio.Std
|
|
open Wayland
|
|
|
|
(* Since we're just relaying messages, we mostly don't care about checking version compatibility.
|
|
e.g. if a client sends us a v5 message, then we can assume the corresponding server object
|
|
supports v5 too (otherwise the client shouldn't have sent it).
|
|
So we just cast away version contraints using [cv]. *)
|
|
let cv = Proxy.cast_version
|
|
|
|
|
|
type surface_data = ..
|
|
|
|
type surface_data += No_surface_data
|
|
|
|
type xwayland_hooks = <
|
|
on_create_surface :
|
|
'v. ([< `V1 | `V2 | `V3 | `V4 | `V5 | `V6] as 'v) H.Wl_surface.t -> 'v C.Wl_surface.t ->
|
|
set_configured:([`Show | `Hide | `Unmanaged] -> unit) ->
|
|
unit;
|
|
|
|
on_destroy_surface :
|
|
'v. ([< `V1 | `V2 | `V3 | `V4 | `V5 | `V6] as 'v) H.Wl_surface.t ->
|
|
unit;
|
|
|
|
on_pointer_entry : 'v.
|
|
surface:([< `V1 | `V2 | `V3 | `V4 | `V5 | `V6] as 'v) H.Wl_surface.t ->
|
|
forward_event:(unit -> unit) ->
|
|
unit;
|
|
|
|
on_keyboard_entry : 'v.
|
|
surface:([< `V1 | `V2 | `V3 | `V4 | `V5 | `V6] as 'v) H.Wl_surface.t ->
|
|
forward_event:(unit -> unit) ->
|
|
unit;
|
|
|
|
on_keyboard_leave : 'v.
|
|
surface:([< `V1 | `V2 | `V3 | `V4 | `V5 | `V6] as 'v) H.Wl_surface.t ->
|
|
unit;
|
|
|
|
set_ping : (unit -> unit) -> unit;
|
|
|
|
scale : int32;
|
|
>
|
|
|
|
let scale_to_client ~xwayland (x, y) =
|
|
match xwayland with
|
|
| None -> (x, y)
|
|
| Some xw ->
|
|
let scale = xw#scale in
|
|
(Int32.mul x scale, Int32.mul y scale)
|
|
|
|
let scale_to_host ~xwayland (x, y) =
|
|
match xwayland with
|
|
| None -> (x, y)
|
|
| Some xw ->
|
|
let scale = xw#scale in
|
|
(Int32.div x scale, Int32.div y scale)
|
|
|
|
let point_to_client ~xwayland (x, y) =
|
|
match xwayland with
|
|
| None -> (x, y)
|
|
| Some xw ->
|
|
let scale = xw#scale in
|
|
if scale = 1l then (x, y)
|
|
else (
|
|
Fixed.of_bits (Int32.mul (Fixed.to_bits x) scale),
|
|
Fixed.of_bits (Int32.mul (Fixed.to_bits y) scale)
|
|
)
|
|
|
|
type t = {
|
|
host : Host.t;
|
|
config : Config.t;
|
|
}
|
|
|
|
let update_serial t serial = t.host.last_serial <- serial
|
|
|
|
(* Data attached to host objects (e.g. the corresponding client object).
|
|
Host and client versions are assumed to match. *)
|
|
module HD = struct
|
|
type 'v surface = {
|
|
client : 'v C.Wl_surface.t;
|
|
mutable data : surface_data;
|
|
}
|
|
|
|
type 'a t =
|
|
| Surface : 'v surface -> [`Wl_surface] t
|
|
| Data_offer : 'v C.Wl_data_offer.t -> [`Wl_data_offer] t
|
|
| Gtk_data_offer : 'v C.Gtk_primary_selection_offer.t -> [`Zwp_primary_selection_offer_v1] t
|
|
| Zwp_data_offer : 'v C.Zwp_primary_selection_offer_v1.t -> [`Zwp_primary_selection_offer_v1] t
|
|
| Output : 'v C.Wl_output.t -> [`Wl_output] t
|
|
end
|
|
|
|
(* Data attached to client objects (e.g. the corresponding host object).
|
|
Host and client versions are assumed to match. *)
|
|
module CD = struct
|
|
type 'v virtwl_buffer = {
|
|
host_buffer : 'v H.Wl_buffer.t;
|
|
host_memory : Cstruct.t;
|
|
client_memory : Cstruct.t;
|
|
mapping_incr : unit -> unit;
|
|
mapping_decr : unit -> unit;
|
|
}
|
|
|
|
type 'v buffer = [
|
|
| `Virtwl of 'v virtwl_buffer Lazy.t
|
|
| `Direct of 'v H.Wl_buffer.t
|
|
]
|
|
|
|
type surface_state =
|
|
| Ready
|
|
| Unconfigured of (unit -> unit) Queue.t (* Events to forward once configured *)
|
|
| Destroyed
|
|
|
|
type 'v surface = {
|
|
host_surface : 'v H.Wl_surface.t;
|
|
mutable state : surface_state;
|
|
mutable host_memory : Cstruct.t;
|
|
mutable client_memory : Cstruct.t;
|
|
mutable release_mapping : unit -> unit;
|
|
}
|
|
|
|
(* Decoration info is separate so it can be retrieved without existential type issues *)
|
|
type toplevel_decoration = {
|
|
mutable host_decoration : [`V1] H.Zxdg_toplevel_decoration_v1.t option;
|
|
mutable on_decoration_configure : (unit -> unit) option;
|
|
}
|
|
|
|
type 'v toplevel = {
|
|
host_toplevel : 'v H.Xdg_toplevel.t;
|
|
decoration : toplevel_decoration;
|
|
}
|
|
|
|
type 'a t =
|
|
| Region : 'v H.Wl_region.t -> [`Wl_region] t
|
|
| Surface : 'v surface -> [`Wl_surface] t
|
|
| Buffer : 'v buffer -> [`Wl_buffer] t
|
|
| Seat : 'v H.Wl_seat.t -> [`Wl_seat] t
|
|
| Output : 'v H.Wl_output.t -> [`Wl_output] t
|
|
| Toplevel : 'v toplevel -> [`Xdg_toplevel] t
|
|
| Xdg_surface : 'v H.Xdg_surface.t -> [`Xdg_surface] t
|
|
| Xdg_positioner : 'v H.Xdg_positioner.t -> [`Xdg_positioner] t
|
|
| Data_source : 'v H.Wl_data_source.t -> [`Wl_data_source] t
|
|
| Gtk_source : 'v H.Zwp_primary_selection_source_v1.t -> [`Gtk_primary_selection_source] t
|
|
| Pointer : 'v H.Wl_pointer.t -> [`Wl_pointer] t
|
|
| Zwp_source : 'v H.Zwp_primary_selection_source_v1.t -> [`Zwp_primary_selection_source_v1] t
|
|
| Image_description : 'v H.Wp_image_description_v1.t -> [`Wp_image_description_v1] t
|
|
| Image_desc_reference : 'v H.Wp_image_description_reference_v1.t -> [`Wp_image_description_reference_v1] t
|
|
end
|
|
|
|
(* Note: the role here is our role: [`Server] data is attached to proxies to
|
|
our clients (where we are the server), while [`Client] data is attached to host objects. *)
|
|
type ('a, 'role) user_data =
|
|
| Client_data : 'a CD.t -> ('a, [`Server]) user_data
|
|
| Host_data : 'a HD.t -> ('a, [`Client]) user_data
|
|
|
|
type ('a, 'role) Wayland.S.user_data += Relay of ('a, 'role) user_data
|
|
|
|
let host_data x = Relay (Host_data x)
|
|
let client_data x = Relay (Client_data x)
|
|
|
|
let user_data (proxy : ('a, _, 'role) Proxy.t) : ('a, 'role) user_data =
|
|
match Wayland.Proxy.user_data proxy with
|
|
| Relay x -> x
|
|
| S.No_data -> Fmt.failwith "No data attached to %a!" Proxy.pp proxy
|
|
| _ -> Fmt.failwith "Unexpected data attached to %a!" Proxy.pp proxy
|
|
|
|
let to_client (type a) (h : (a, 'v, [`Client]) Proxy.t) : (a, 'v, [`Server]) Proxy.t =
|
|
let cv = Proxy.cast_version in
|
|
let Host_data data = user_data h in
|
|
let open HD in
|
|
match data with
|
|
| Output c -> cv c
|
|
| Surface c -> cv c.client
|
|
| Data_offer c -> cv c
|
|
| Zwp_data_offer c -> cv c
|
|
| Gtk_data_offer _ ->
|
|
(* Here, a client Gtk corresponds to a host Zwp, so the types aren't right. *)
|
|
failwith "Can't use to_client with GTK translation"
|
|
|
|
let to_host (type a) (c : (a, 'v, [`Server]) Proxy.t) : (a, 'v, [`Client]) Proxy.t =
|
|
let cv = Proxy.cast_version in
|
|
let Client_data data = user_data c in
|
|
match data with
|
|
| Surface x -> cv x.host_surface
|
|
| Seat x -> cv x
|
|
| Output x -> cv x
|
|
| Region x -> cv x
|
|
| Toplevel x -> cv x.host_toplevel
|
|
| Xdg_surface x -> cv x
|
|
| Xdg_positioner x -> cv x
|
|
| Data_source x -> cv x
|
|
| Zwp_source x -> cv x
|
|
| Buffer (`Virtwl x) -> cv (Lazy.force x).host_buffer
|
|
| Buffer (`Direct x) -> cv x
|
|
| Pointer c -> cv c
|
|
| Image_description x -> cv x
|
|
| Image_desc_reference x -> cv x
|
|
| Gtk_source _ ->
|
|
(* Here, a client Gtk corresponds to a host Zwp, so the types aren't right. *)
|
|
failwith "Can't use to_host with GTK translation"
|
|
|
|
let get_toplevel_decoration (c : ([`Xdg_toplevel], _, [`Server]) Proxy.t) : CD.toplevel_decoration =
|
|
let Client_data (Toplevel data) = user_data c in
|
|
data.decoration
|
|
|
|
(* When the client asks to destroy something, delay the ack until the host object is destroyed.
|
|
This means the client sees events in the usual order, and means we can continue forwarding
|
|
any events the host sends before hearing about the deletion. *)
|
|
let delete_with fn host client =
|
|
Proxy.on_delete host (fun () -> if Proxy.transport_up client then Proxy.delete client);
|
|
fn host
|
|
|
|
let make_region ~host_region r =
|
|
let h = host_region @@ new H.Wl_region.v1 in
|
|
let user_data = client_data (Region h) in
|
|
Proxy.Handler.attach r @@ object
|
|
inherit [_] C.Wl_region.v1
|
|
method! user_data = user_data
|
|
method on_add _ = H.Wl_region.add h
|
|
method on_subtract _ = H.Wl_region.subtract h
|
|
method on_destroy = delete_with H.Wl_region.destroy h
|
|
end
|
|
|
|
(* wl_shm memory buffers are allocated by the client inside the guest and
|
|
cannot be shared directly with the host. Instead, we allocate some host
|
|
memory of the same size, map that into the guest, and copy the data across
|
|
as needed.
|
|
|
|
Xwayland likes to create huge numbers of mappings and then destroy them
|
|
without ever using the buffers for anything, so to avoid the expense of
|
|
mapping and unmapping pools that are never used, we map them lazily.
|
|
|
|
We assume that when a pool is resized the client will recreate all the
|
|
buffers, which might not always be true, but seems to be working so far. *)
|
|
module Shm : sig
|
|
type t
|
|
(** A proxy for a pair of memory pools. *)
|
|
|
|
type buffer
|
|
(** A region within the pools. *)
|
|
|
|
val create :
|
|
host_shm:([< `V1|`V2] as 'v) H.Wl_shm.t ->
|
|
virtio_gpu:Virtio_gpu.t ->
|
|
client_fd:Unix.file_descr ->
|
|
size:int32 ->
|
|
'v C.Wl_shm_pool.t ->
|
|
t
|
|
(** [create ~host_shm ~virtio_gpu ~client_fd ~size proxy] is a pool proxy that creates a host pool of size [size],
|
|
and maps that and [client_fd] into our address space.
|
|
@param virtio_gpu Used to create a memory region that can be shared with the host.
|
|
@param host_shm Used to notify the host compositor about the new region.
|
|
@param client_fd Used to map the client's memory. Will be closed when the ref-count reaches zero.
|
|
@param proxy [client_fd] is closed when this and all buffers have been destroyed. *)
|
|
|
|
val resize : t -> int32 -> unit
|
|
|
|
val create_buffer : t ->
|
|
offset:int32 ->
|
|
width:int32 ->
|
|
height:int32 ->
|
|
stride:int32 ->
|
|
format:Protocols.Wl_shm.Format.t ->
|
|
[`V1] C.Wl_buffer.t ->
|
|
buffer
|
|
(** [create_buffer t ... proxy] allocates a region of [t].
|
|
@param proxy Will receive [release] events from the compositor if attached. *)
|
|
|
|
val destroy_buffer : buffer -> unit
|
|
(** [destroy_buffer b] destroys the host buffer (if any), and notifies the client proxy of the deletion. *)
|
|
|
|
val user_data : buffer -> [`V1] CD.buffer
|
|
(** [user_data b] is some data to attach to the client proxy so the surface can find it. *)
|
|
|
|
val map_buffer : 'v CD.virtwl_buffer Lazy.t -> 'v CD.virtwl_buffer
|
|
(** [map_buffer user_data] is used by the surface when attaching the buffer. *)
|
|
end = struct
|
|
type mapping = {
|
|
host_pool : [`V1|`V2] H.Wl_shm_pool.t;
|
|
client_memory_pool : Cstruct.buffer; (* The client's memory mapped into our address space *)
|
|
host_memory_pool : Cstruct.buffer; (* The host's memory mapped into our address space *)
|
|
mutable ref_count : int; (* Number of forced buffers using this mapping *)
|
|
mutable detached : bool; (* True once removed from t.mapping *)
|
|
}
|
|
|
|
let unmap_mapping m =
|
|
Virtio_gpu.Utils.unmap (Bigarray.genarray_of_array1 m.host_memory_pool);
|
|
Virtio_gpu.Utils.unmap (Bigarray.genarray_of_array1 m.client_memory_pool)
|
|
|
|
let maybe_unmap_mapping m =
|
|
if m.detached && m.ref_count = 0 then
|
|
unmap_mapping m
|
|
|
|
let mapping_dec_ref m =
|
|
assert (m.ref_count > 0);
|
|
m.ref_count <- m.ref_count - 1;
|
|
maybe_unmap_mapping m
|
|
|
|
type t = {
|
|
host_shm : [`V1|`V2] H.Wl_shm.t;
|
|
virtio_gpu : Virtio_gpu.t;
|
|
mutable size : int32;
|
|
mutable client_fd : Unix.file_descr option; (* [client_fd = None <=> ref_count = 0 *)
|
|
mutable ref_count : int; (* The number of client proxies (pool + buffers) active *)
|
|
mutable mapping : mapping option; (* If [None] then map when needed *)
|
|
}
|
|
|
|
let with_memory_fd t ~size fn =
|
|
let query = {
|
|
Virtio_gpu.Dev.
|
|
width = Int32.of_int size;
|
|
height = 1l;
|
|
drm_format = Virtio_gpu.Drm_format.r8;
|
|
} in
|
|
let image = Virtio_gpu.alloc t.virtio_gpu query in
|
|
match fn image with
|
|
| x -> Unix.close image.fd; x
|
|
| exception ex -> Unix.close image.fd; raise ex
|
|
|
|
(* This is called when we attach a buffer to a surface
|
|
(so the client-side buffer proxy must still exist). *)
|
|
let get_mapping t =
|
|
assert (t.ref_count > 0);
|
|
match t.mapping with
|
|
| Some m -> m
|
|
| None ->
|
|
let client_fd = Option.get t.client_fd in (* OK because ref_count > 0 *)
|
|
let size = Int32.to_int t.size in
|
|
let client_memory_pool = Unix.map_file client_fd Bigarray.Char Bigarray.c_layout true [| size |] in
|
|
let host_pool, host_memory_pool =
|
|
with_memory_fd t ~size (fun { Virtio_gpu.Dev.fd; host_size; offset; _ } ->
|
|
let host_pool = H.Wl_shm.create_pool t.host_shm ~fd ~size:t.size @@ new H.Wl_shm_pool.v1 in
|
|
let host_memory = Virtio_gpu.Utils.safe_map_file fd
|
|
~kind:Bigarray.Char
|
|
~len:size
|
|
~host_size:(Int64.to_int host_size)
|
|
~pos:(Int64.of_int32 offset)
|
|
in
|
|
host_pool, host_memory
|
|
)
|
|
in
|
|
let client_memory_pool = Bigarray.array1_of_genarray client_memory_pool in
|
|
let m = { host_pool; client_memory_pool; host_memory_pool; ref_count = 0; detached = false } in
|
|
t.mapping <- Some m;
|
|
m
|
|
|
|
type buffer = {
|
|
data : [`V1] CD.virtwl_buffer Lazy.t; (* Forced when buffer is attached to a surface *)
|
|
on_destroy : unit Lazy.t; (* Forced when client buffer proxy is destroyed *)
|
|
}
|
|
|
|
let user_data b : _ CD.buffer = `Virtwl b.data
|
|
|
|
let clear_mapping t =
|
|
t.mapping |> Option.iter (fun m ->
|
|
if Proxy.transport_up m.host_pool then
|
|
H.Wl_shm_pool.destroy m.host_pool;
|
|
m.detached <- true;
|
|
maybe_unmap_mapping m;
|
|
t.mapping <- None
|
|
)
|
|
|
|
let resize t new_size =
|
|
if t.size <> new_size then (
|
|
t.size <- new_size;
|
|
clear_mapping t (* Will force a new mapping if used in future *)
|
|
)
|
|
|
|
let dec_ref t =
|
|
assert (t.ref_count > 0);
|
|
t.ref_count <- t.ref_count - 1;
|
|
if t.ref_count = 0 then (
|
|
Unix.close (Option.get t.client_fd);
|
|
t.client_fd <- None;
|
|
clear_mapping t
|
|
)
|
|
|
|
let create_buffer t ~offset ~width ~height ~stride ~format buffer : buffer =
|
|
assert (t.ref_count > 0); (* The shm_pool proxy must exist to call this. *)
|
|
t.ref_count <- t.ref_count + 1;
|
|
Proxy.on_delete buffer (fun () -> dec_ref t);
|
|
let mapping_used = ref None in
|
|
let data =
|
|
lazy (
|
|
(* Forced by [map_buffer] when the the buffer is attached to a surface,
|
|
so buffer proxy still exists. *)
|
|
let len = Int32.to_int height * Int32.to_int stride in
|
|
let mapping = get_mapping t in
|
|
mapping.ref_count <- mapping.ref_count + 1;
|
|
mapping_used := Some mapping;
|
|
let host_memory = Cstruct.of_bigarray mapping.host_memory_pool ~off:(Int32.to_int offset) ~len in
|
|
let client_memory = Cstruct.of_bigarray mapping.client_memory_pool ~off:(Int32.to_int offset) ~len in
|
|
let host_buffer =
|
|
H.Wl_shm_pool.create_buffer mapping.host_pool ~offset ~width ~height ~stride ~format
|
|
@@ object
|
|
inherit [_] H.Wl_buffer.v1
|
|
method on_release _ = C.Wl_buffer.release buffer
|
|
end
|
|
in
|
|
{ CD.host_memory; client_memory; host_buffer;
|
|
mapping_incr = (fun () -> mapping.ref_count <- mapping.ref_count + 1);
|
|
mapping_decr = (fun () -> mapping_dec_ref mapping) }
|
|
)
|
|
in
|
|
let on_destroy = lazy (
|
|
if Lazy.is_val data then (
|
|
(match !mapping_used with Some m -> mapping_dec_ref m | None -> ());
|
|
delete_with H.Wl_buffer.destroy (Lazy.force data).host_buffer buffer
|
|
) else (
|
|
Proxy.delete buffer
|
|
)
|
|
) in
|
|
{ on_destroy; data }
|
|
|
|
(* Client-side buffer proxy must still exist when this is called. *)
|
|
let map_buffer : _ -> _ CD.virtwl_buffer = Lazy.force
|
|
|
|
let destroy_buffer b =
|
|
Lazy.force b.on_destroy
|
|
|
|
let create ~host_shm ~virtio_gpu ~client_fd ~size client_shm =
|
|
let t = {
|
|
host_shm = (host_shm :> [`V1|`V2] H.Wl_shm.t);
|
|
virtio_gpu;
|
|
size;
|
|
client_fd = Some client_fd;
|
|
ref_count = 1;
|
|
mapping = None;
|
|
} in
|
|
Proxy.on_delete client_shm (fun () -> dec_ref t);
|
|
t
|
|
end
|
|
|
|
let make_surface ~xwayland ~host_surface c =
|
|
let c = cv c in
|
|
let h =
|
|
let user_data = host_data (HD.Surface { HD.client = c; data = No_surface_data }) in
|
|
host_surface @@ object
|
|
inherit [_] H.Wl_surface.v1
|
|
method! user_data = user_data
|
|
method on_enter _ ~output = C.Wl_surface.enter c ~output:(to_client output)
|
|
method on_leave _ ~output = C.Wl_surface.leave c ~output:(to_client output)
|
|
method on_preferred_buffer_scale _ = C.Wl_surface.preferred_buffer_scale c
|
|
method on_preferred_buffer_transform _ = C.Wl_surface.preferred_buffer_transform c
|
|
end
|
|
in
|
|
let h = Proxy.cast_version h in
|
|
let data =
|
|
let state = if xwayland = None then CD.Ready else Unconfigured (Queue.create ()) in
|
|
{ CD.host_surface = h; host_memory = Cstruct.empty; client_memory = Cstruct.empty; state;
|
|
release_mapping = ignore }
|
|
in
|
|
let when_configured fn =
|
|
match data.state with
|
|
| Ready -> fn ()
|
|
| Unconfigured q -> Queue.add fn q
|
|
| Destroyed -> ()
|
|
in
|
|
let state = ref `Show in (* X11 hidden windows get [`Hide] here *)
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_surface.v1
|
|
method! user_data = client_data (Surface data)
|
|
|
|
method on_attach _ ~buffer ~x ~y =
|
|
let (x, y) = scale_to_host ~xwayland (x, y) in
|
|
when_configured @@ fun () ->
|
|
match buffer with
|
|
| Some buffer when !state <> `Hide ->
|
|
let Client_data (Buffer buffer) = user_data buffer in
|
|
let host_buffer =
|
|
match buffer with
|
|
| `Direct host_buffer ->
|
|
data.release_mapping ();
|
|
data.release_mapping <- ignore;
|
|
data.host_memory <- Cstruct.empty;
|
|
data.client_memory <- Cstruct.empty;
|
|
host_buffer
|
|
| `Virtwl buffer ->
|
|
let buffer = Shm.map_buffer buffer in
|
|
data.release_mapping ();
|
|
buffer.mapping_incr ();
|
|
data.release_mapping <- buffer.mapping_decr;
|
|
data.host_memory <- buffer.host_memory;
|
|
data.client_memory <- buffer.client_memory;
|
|
buffer.host_buffer
|
|
in
|
|
H.Wl_surface.attach h ~buffer:(Some host_buffer) ~x ~y
|
|
| _ ->
|
|
data.release_mapping ();
|
|
data.release_mapping <- ignore;
|
|
data.host_memory <- Cstruct.empty;
|
|
data.client_memory <- Cstruct.empty;
|
|
H.Wl_surface.attach h ~buffer:None ~x ~y
|
|
|
|
method on_commit _ =
|
|
when_configured @@ fun () ->
|
|
(* todo: only copy the bit that changed *)
|
|
Cstruct.blit data.client_memory 0 data.host_memory 0 (Cstruct.length data.client_memory);
|
|
H.Wl_surface.commit h
|
|
|
|
method on_damage _ ~x ~y ~width ~height =
|
|
when_configured @@ fun () ->
|
|
let (x, y) = scale_to_host ~xwayland (x, y) in
|
|
let (width, height) = scale_to_host ~xwayland (width, height) in
|
|
H.Wl_surface.damage h ~x ~y ~width ~height
|
|
|
|
method on_damage_buffer _ ~x ~y ~width ~height =
|
|
when_configured @@ fun () ->
|
|
H.Wl_surface.damage_buffer h ~x ~y ~width ~height
|
|
|
|
method on_destroy =
|
|
data.release_mapping ();
|
|
data.release_mapping <- ignore;
|
|
data.state <- Destroyed;
|
|
xwayland |> Option.iter (fun (x:xwayland_hooks) -> x#on_destroy_surface h);
|
|
delete_with H.Wl_surface.destroy h
|
|
|
|
method on_frame _ callback =
|
|
when_configured @@ fun () ->
|
|
let _ : _ Proxy.t = H.Wl_surface.frame h @@ Wayland.callback @@ fun callback_data ->
|
|
C.Wl_callback.done_ callback ~callback_data;
|
|
Proxy.delete callback
|
|
in
|
|
Proxy.Handler.attach callback @@ new C.Wl_callback.v1
|
|
|
|
method on_set_input_region _ ~region =
|
|
when_configured @@ fun () ->
|
|
H.Wl_surface.set_input_region h ~region:(Option.map to_host region)
|
|
|
|
method on_set_opaque_region _ ~region =
|
|
when_configured @@ fun () ->
|
|
H.Wl_surface.set_opaque_region h ~region:(Option.map to_host region)
|
|
|
|
method on_set_buffer_scale _ ~scale =
|
|
when_configured @@ fun () ->
|
|
H.Wl_surface.set_buffer_scale h ~scale
|
|
|
|
method on_set_buffer_transform _ ~transform =
|
|
when_configured @@ fun () ->
|
|
H.Wl_surface.set_buffer_transform h ~transform
|
|
|
|
method on_offset _ ~x ~y =
|
|
when_configured @@ fun () ->
|
|
let (x, y) = scale_to_host ~xwayland (x, y) in
|
|
H.Wl_surface.offset h ~x ~y
|
|
end;
|
|
xwayland |> Option.iter (fun (x:xwayland_hooks) ->
|
|
if x#scale <> 1l then
|
|
H.Wl_surface.set_buffer_scale h ~scale:x#scale; (* Xwayland will be a new enough version *)
|
|
let set_configured s =
|
|
if s = `Unmanaged && x#scale <> 1l then (
|
|
(* For pointer cursors we want them at the normal size, even if low-res.
|
|
Also, Vim tries to hide the pointer by setting a 1x1 cursor, which confuses things
|
|
when unscaled. Ideally we would stop doing transforms in this case, but it doesn't
|
|
seem to matter. *)
|
|
H.Wl_surface.set_buffer_scale h ~scale:1l;
|
|
);
|
|
state := s;
|
|
match data.state with
|
|
| Ready | Destroyed -> ()
|
|
| Unconfigured q ->
|
|
data.state <- Ready;
|
|
Queue.iter (fun f -> f ()) q
|
|
in
|
|
x#on_create_surface h c ~set_configured
|
|
)
|
|
|
|
let set_surface_data surface data =
|
|
let Host_data (HD.Surface x) = user_data surface in
|
|
x.data <- data
|
|
|
|
let get_surface_data surface =
|
|
let Host_data (HD.Surface x) = user_data surface in
|
|
x.data
|
|
|
|
let make_compositor ~xwayland bind proxy =
|
|
let h = bind @@ new H.Wl_compositor.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wl_compositor.v1
|
|
method on_create_region _ = make_region ~host_region:(H.Wl_compositor.create_region h)
|
|
method on_create_surface _ = make_surface ~xwayland ~host_surface:(H.Wl_compositor.create_surface h)
|
|
end
|
|
|
|
let make_subsurface ~xwayland ~host_subsurface c =
|
|
let h = host_subsurface @@ new H.Wl_subsurface.v1 in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_subsurface.v1
|
|
method on_destroy = delete_with H.Wl_subsurface.destroy h
|
|
method on_place_above _ ~sibling = H.Wl_subsurface.place_above h ~sibling:(to_host sibling)
|
|
method on_place_below _ ~sibling = H.Wl_subsurface.place_below h ~sibling:(to_host sibling)
|
|
method on_set_desync _ = H.Wl_subsurface.set_desync h
|
|
|
|
method on_set_position _ ~x ~y =
|
|
let (x, y) = scale_to_host ~xwayland (x, y) in
|
|
H.Wl_subsurface.set_position h ~x ~y
|
|
|
|
method on_set_sync _ = H.Wl_subsurface.set_sync h
|
|
end
|
|
|
|
let make_subcompositor ~xwayland bind proxy =
|
|
let h = bind @@ new H.Wl_subcompositor.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wl_subcompositor.v1
|
|
method on_destroy = delete_with H.Wl_subcompositor.destroy h
|
|
|
|
method on_get_subsurface _ subsurface ~surface ~parent =
|
|
let surface = to_host surface in
|
|
let parent = to_host parent in
|
|
let host_subsurface = H.Wl_subcompositor.get_subsurface h ~surface ~parent in
|
|
make_subsurface ~xwayland ~host_subsurface subsurface
|
|
end
|
|
|
|
let make_buffer b proxy =
|
|
let user_data = client_data (Buffer (Shm.user_data b)) in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wl_buffer.v1
|
|
method! user_data = user_data
|
|
method on_destroy _ = Shm.destroy_buffer b
|
|
end
|
|
|
|
(* todo: this all needs to be more robust.
|
|
Also, sealing? *)
|
|
let make_shm_pool_virtwl ~virtio_gpu ~host_shm proxy ~fd:client_fd ~size:orig_size =
|
|
let mapping = Shm.create ~host_shm ~virtio_gpu ~client_fd ~size:orig_size proxy in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wl_shm_pool.v1
|
|
|
|
method on_create_buffer _ buffer ~offset ~width ~height ~stride ~format =
|
|
let b = Shm.create_buffer mapping ~offset ~width ~height ~stride ~format buffer in
|
|
make_buffer b buffer
|
|
|
|
method on_destroy t = Proxy.delete t
|
|
|
|
method on_resize _ ~size = Shm.resize mapping size
|
|
end
|
|
|
|
let make_shm_pool_direct host_pool proxy =
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wl_shm_pool.v1
|
|
|
|
method on_create_buffer _ buffer ~offset ~width ~height ~stride ~format =
|
|
let host_buffer = H.Wl_shm_pool.create_buffer host_pool ~offset ~width ~height ~stride ~format @@ object
|
|
inherit [_] H.Wl_buffer.v1
|
|
method on_release _ = C.Wl_buffer.release buffer
|
|
end
|
|
in
|
|
Proxy.Handler.attach buffer @@ object
|
|
inherit [_] C.Wl_buffer.v1
|
|
method! user_data = client_data (Buffer (`Direct host_buffer))
|
|
method on_destroy = delete_with H.Wl_buffer.destroy host_buffer
|
|
end
|
|
|
|
method on_destroy _ = H.Wl_shm_pool.destroy host_pool
|
|
|
|
method on_resize _ = H.Wl_shm_pool.resize host_pool
|
|
end
|
|
|
|
let make_output ~xwayland bind c =
|
|
let c = Proxy.cast_version c in
|
|
let h =
|
|
let user_data = host_data (HD.Output c) in
|
|
bind @@ object
|
|
inherit [_] H.Wl_output.v1
|
|
method! user_data = user_data
|
|
method on_done _ = C.Wl_output.done_ (Proxy.cast_version c)
|
|
method on_geometry _ = C.Wl_output.geometry c
|
|
method on_mode _ = C.Wl_output.mode c
|
|
method on_name _ ~name = C.Wl_output.name c ~name
|
|
method on_description _ ~description = C.Wl_output.description c ~description
|
|
|
|
method on_scale _ ~factor =
|
|
let factor =
|
|
match xwayland with
|
|
| Some x -> Int32.div factor x#scale
|
|
| None -> factor
|
|
in
|
|
C.Wl_output.scale (Proxy.cast_version c) ~factor
|
|
end
|
|
in
|
|
let user_data = client_data (Output h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_output.v1
|
|
method! user_data = user_data
|
|
method on_release = delete_with H.Wl_output.release (cv h)
|
|
end
|
|
|
|
let make_pointer t ~xwayland ~host_seat c =
|
|
let c = cv c in
|
|
let h : _ Proxy.t = H.Wl_seat.get_pointer host_seat @@ object
|
|
inherit [_] H.Wl_pointer.v1
|
|
method on_axis _ = C.Wl_pointer.axis c
|
|
method on_axis_discrete _ = C.Wl_pointer.axis_discrete c
|
|
method on_axis_source _ = C.Wl_pointer.axis_source c
|
|
method on_axis_stop _ = C.Wl_pointer.axis_stop c
|
|
method on_axis_value120 _ = C.Wl_pointer.axis_value120 c
|
|
|
|
method on_button _ ~serial ~time ~button ~state =
|
|
update_serial t serial;
|
|
C.Wl_pointer.button c ~serial ~time ~button ~state
|
|
|
|
method on_enter _ ~serial ~surface ~surface_x ~surface_y =
|
|
update_serial t serial;
|
|
let (surface_x, surface_y) = point_to_client ~xwayland (surface_x, surface_y) in
|
|
let forward_event () =
|
|
C.Wl_pointer.enter c ~serial ~surface:(to_client surface) ~surface_x ~surface_y
|
|
in
|
|
match xwayland with
|
|
| None -> forward_event ()
|
|
| Some (xwayland:xwayland_hooks) ->
|
|
xwayland#on_pointer_entry ~surface ~forward_event
|
|
|
|
method on_leave _ ~serial ~surface =
|
|
update_serial t serial;
|
|
C.Wl_pointer.leave c ~serial ~surface:(to_client surface)
|
|
|
|
method on_motion _ ~time ~surface_x ~surface_y =
|
|
let (surface_x, surface_y) = point_to_client ~xwayland (surface_x, surface_y) in
|
|
C.Wl_pointer.motion c ~time ~surface_x ~surface_y
|
|
|
|
method on_frame _ = C.Wl_pointer.frame c
|
|
|
|
method on_axis_relative_direction _ = C.Wl_pointer.axis_relative_direction c
|
|
end
|
|
in
|
|
let user_data = client_data (CD.Pointer h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_pointer.v1
|
|
method! user_data = user_data
|
|
|
|
method on_set_cursor _ ~serial ~surface ~hotspot_x ~hotspot_y =
|
|
(* Cursors are not unscaled, so no need to transform here. *)
|
|
H.Wl_pointer.set_cursor h ~serial ~surface:(Option.map to_host surface) ~hotspot_x ~hotspot_y
|
|
|
|
method on_release t =
|
|
delete_with H.Wl_pointer.release h t
|
|
end
|
|
|
|
let make_keyboard t ~xwayland ~host_seat c =
|
|
let h : _ Proxy.t = H.Wl_seat.get_keyboard host_seat @@ object
|
|
inherit [_] H.Wl_keyboard.v1
|
|
|
|
method on_keymap _ ~format ~fd ~size =
|
|
C.Wl_keyboard.keymap c ~format ~fd ~size;
|
|
Unix.close fd
|
|
|
|
method on_enter _ ~serial ~surface ~keys =
|
|
update_serial t serial;
|
|
let forward_event () =
|
|
C.Wl_keyboard.enter c ~serial ~surface:(to_client surface) ~keys
|
|
in
|
|
match xwayland with
|
|
| None -> forward_event ()
|
|
| Some (xwayland:xwayland_hooks) ->
|
|
xwayland#on_keyboard_entry ~surface ~forward_event
|
|
|
|
method on_leave _ ~serial ~surface =
|
|
update_serial t serial;
|
|
C.Wl_keyboard.leave c ~serial ~surface:(to_client surface);
|
|
xwayland |> Option.iter (fun (xwayland : xwayland_hooks) ->
|
|
xwayland#on_keyboard_leave ~surface
|
|
)
|
|
|
|
method on_key _ ~serial ~time ~key ~state =
|
|
update_serial t serial;
|
|
C.Wl_keyboard.key c ~serial ~time ~key ~state
|
|
|
|
method on_modifiers _ ~serial ~mods_depressed ~mods_latched ~mods_locked ~group =
|
|
update_serial t serial;
|
|
C.Wl_keyboard.modifiers c ~serial ~mods_depressed ~mods_latched ~mods_locked ~group
|
|
|
|
method on_repeat_info _ = C.Wl_keyboard.repeat_info (cv c)
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_keyboard.v1
|
|
method on_release = delete_with H.Wl_keyboard.release h
|
|
end
|
|
|
|
let make_seat ~xwayland t bind c =
|
|
let c = Proxy.cast_version c in
|
|
let cap_mask = C.Wl_seat.Capability.(Int32.logor keyboard pointer) in
|
|
let host = bind @@ object
|
|
inherit [_] H.Wl_seat.v1
|
|
|
|
method on_capabilities _ ~capabilities =
|
|
C.Wl_seat.capabilities c ~capabilities:(Int32.logand capabilities cap_mask)
|
|
method on_name _ = C.Wl_seat.name (cv c)
|
|
end
|
|
in
|
|
let host = cv host in
|
|
let user_data = client_data (Seat host) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_seat.v1
|
|
method! user_data = user_data
|
|
method on_get_keyboard _ keyboard = make_keyboard ~xwayland t ~host_seat:host keyboard
|
|
method on_get_pointer _ c = make_pointer ~xwayland t ~host_seat:host c
|
|
method on_get_touch _ = Fmt.failwith "TODO: on_get_touch"
|
|
method on_release = delete_with H.Wl_seat.release host
|
|
end
|
|
|
|
let make_shm ~virtio_gpu bind c =
|
|
let c = Proxy.cast_version c in
|
|
let h = bind @@ object
|
|
inherit [_] H.Wl_shm.v1
|
|
method on_format _ = C.Wl_shm.format c
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_shm.v1
|
|
method on_create_pool _ proxy ~fd ~size =
|
|
match virtio_gpu with
|
|
| Some virtio_gpu -> make_shm_pool_virtwl ~virtio_gpu ~host_shm:h proxy ~fd ~size
|
|
| None ->
|
|
let host_pool = H.Wl_shm.create_pool h ~fd ~size @@ new H.Wl_shm_pool.v1 in
|
|
Unix.close fd;
|
|
make_shm_pool_direct host_pool proxy
|
|
|
|
method on_release = delete_with H.Wl_shm.release h
|
|
end
|
|
|
|
let make_popup ~host_popup c =
|
|
let h = host_popup @@ object
|
|
inherit [_] H.Xdg_popup.v1
|
|
method on_popup_done _ = C.Xdg_popup.popup_done c
|
|
method on_configure _ = C.Xdg_popup.configure c
|
|
method on_repositioned _ = C.Xdg_popup.repositioned c
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Xdg_popup.v1
|
|
method on_destroy = delete_with H.Xdg_popup.destroy h
|
|
method on_grab _ ~seat = H.Xdg_popup.grab h ~seat:(to_host seat)
|
|
method on_reposition _ ~positioner = H.Xdg_popup.reposition h ~positioner:(to_host positioner)
|
|
end
|
|
|
|
let make_toplevel ~registry ~tag ~host_toplevel c =
|
|
let h = host_toplevel @@ object
|
|
inherit [_] H.Xdg_toplevel.v1
|
|
method on_close _ = C.Xdg_toplevel.close c
|
|
method on_configure _ = C.Xdg_toplevel.configure c
|
|
method on_configure_bounds _ = C.Xdg_toplevel.configure_bounds c
|
|
method on_wm_capabilities _ = C.Xdg_toplevel.wm_capabilities c
|
|
end
|
|
in
|
|
let decoration_info = {
|
|
CD.host_decoration = None;
|
|
on_decoration_configure = None;
|
|
} in
|
|
let toplevel_data = {
|
|
CD.host_toplevel = h;
|
|
decoration = decoration_info;
|
|
} in
|
|
(* Create decoration on host to force server-side decorations *)
|
|
begin match Registry.get registry Protocols.Zxdg_decoration_manager_v1.interface with
|
|
| [] ->
|
|
Log.warn (fun f -> f "Host doesn't support %s - server-side decorations may not be enforced" Protocols.Zxdg_decoration_manager_v1.interface)
|
|
| { Registry.name; version } :: _ ->
|
|
let decor_mgr = H.Wl_registry.bind (Registry.wl_registry registry) ~name (object
|
|
inherit [_] H.Zxdg_decoration_manager_v1.v1
|
|
end, min 1l version)
|
|
in
|
|
let decor = H.Zxdg_decoration_manager_v1.get_toplevel_decoration decor_mgr ~toplevel:h @@ object
|
|
inherit [_] H.Zxdg_toplevel_decoration_v1.v1
|
|
method on_configure _ ~mode:_ =
|
|
match decoration_info.on_decoration_configure with
|
|
| Some f -> f ()
|
|
| None -> ()
|
|
end
|
|
in
|
|
decoration_info.host_decoration <- Some decor;
|
|
H.Zxdg_toplevel_decoration_v1.set_mode decor ~mode:H.Zxdg_toplevel_decoration_v1.Mode.Server_side
|
|
end;
|
|
let user_data = client_data (Toplevel toplevel_data) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Xdg_toplevel.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Xdg_toplevel.destroy h
|
|
method on_move _ ~seat = H.Xdg_toplevel.move h ~seat:(to_host seat)
|
|
method on_resize _ ~seat = H.Xdg_toplevel.resize h ~seat:(to_host seat)
|
|
method on_set_app_id _ = H.Xdg_toplevel.set_app_id h
|
|
method on_set_fullscreen _ ~output = H.Xdg_toplevel.set_fullscreen h ~output:(Option.map to_host output)
|
|
method on_set_max_size _ = H.Xdg_toplevel.set_max_size h
|
|
method on_set_maximized _ = H.Xdg_toplevel.set_maximized h
|
|
method on_set_min_size _ = H.Xdg_toplevel.set_min_size h
|
|
method on_set_minimized _ = H.Xdg_toplevel.set_minimized h
|
|
method on_set_parent _ ~parent = H.Xdg_toplevel.set_parent h ~parent:(Option.map to_host parent)
|
|
method on_set_title _ ~title = H.Xdg_toplevel.set_title h ~title:(tag ^ title)
|
|
method on_show_window_menu _ ~seat = H.Xdg_toplevel.show_window_menu h ~seat:(to_host seat)
|
|
method on_unset_fullscreen _ = H.Xdg_toplevel.unset_fullscreen h
|
|
method on_unset_maximized _ = H.Xdg_toplevel.unset_maximized h
|
|
end
|
|
|
|
let make_xdg_surface ~registry ~tag ~host_xdg_surface c =
|
|
let c = cv c in
|
|
let h = host_xdg_surface @@ object
|
|
inherit [_] H.Xdg_surface.v1
|
|
method on_configure _ = C.Xdg_surface.configure c
|
|
end
|
|
in
|
|
let user_data = client_data (Xdg_surface h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Xdg_surface.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Xdg_surface.destroy h
|
|
method on_ack_configure _ = H.Xdg_surface.ack_configure h
|
|
method on_set_window_geometry _ = H.Xdg_surface.set_window_geometry h
|
|
|
|
method on_get_toplevel _ = make_toplevel ~registry ~tag ~host_toplevel:(H.Xdg_surface.get_toplevel h)
|
|
|
|
method on_get_popup _ popup ~parent ~positioner =
|
|
let parent = Option.map to_host parent in
|
|
let positioner = to_host positioner in
|
|
make_popup ~host_popup:(H.Xdg_surface.get_popup h ~parent ~positioner) popup
|
|
end
|
|
|
|
let make_positioner ~host_positioner c =
|
|
let h = host_positioner @@ new H.Xdg_positioner.v1 in
|
|
let user_data = client_data (Xdg_positioner h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Xdg_positioner.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Xdg_positioner.destroy h
|
|
method on_set_anchor _ = H.Xdg_positioner.set_anchor h
|
|
method on_set_anchor_rect _ = H.Xdg_positioner.set_anchor_rect h
|
|
method on_set_constraint_adjustment _ = H.Xdg_positioner.set_constraint_adjustment h
|
|
method on_set_gravity _ = H.Xdg_positioner.set_gravity h
|
|
method on_set_offset _ = H.Xdg_positioner.set_offset h
|
|
method on_set_size _ = H.Xdg_positioner.set_size h
|
|
method on_set_reactive _ = H.Xdg_positioner.set_reactive h
|
|
method on_set_parent_size _ = H.Xdg_positioner.set_parent_size h
|
|
method on_set_parent_configure _ = H.Xdg_positioner.set_parent_configure h
|
|
end
|
|
|
|
let make_xdg_wm_base ~registry ~xwayland ~tag bind proxy =
|
|
let pong_handlers = Queue.create () in
|
|
let h = bind @@ object
|
|
inherit [_] H.Xdg_wm_base.v1
|
|
method on_ping h ~serial =
|
|
Queue.add (H.Xdg_wm_base.pong h) pong_handlers;
|
|
C.Xdg_wm_base.ping proxy ~serial
|
|
end
|
|
in
|
|
let h = Proxy.cast_version h in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Xdg_wm_base.v1
|
|
|
|
method on_destroy = delete_with H.Xdg_wm_base.destroy h
|
|
|
|
method on_pong _ ~serial =
|
|
match Queue.take_opt pong_handlers with
|
|
| Some h -> h ~serial
|
|
| None -> Log.warn (fun f -> f "Ignoring unexpected pong from client!")
|
|
|
|
method on_create_positioner _ = make_positioner ~host_positioner:(H.Xdg_wm_base.create_positioner h)
|
|
|
|
method on_get_xdg_surface _ xdg_surface ~surface =
|
|
let host_xdg_surface = H.Xdg_wm_base.get_xdg_surface h ~surface:(to_host surface) in
|
|
make_xdg_surface ~registry ~tag ~host_xdg_surface xdg_surface
|
|
end;
|
|
xwayland |> Option.iter (fun (x:xwayland_hooks) ->
|
|
x#set_ping (fun () ->
|
|
let serial = 0l in
|
|
let pong, set_pong = Promise.create () in
|
|
Queue.add (fun ~serial:_ -> Promise.resolve set_pong ()) pong_handlers;
|
|
C.Xdg_wm_base.ping proxy ~serial;
|
|
Promise.await pong
|
|
)
|
|
)
|
|
|
|
let make_zxdg_output ~xwayland ~host_xdg_output c =
|
|
let c = cv c in
|
|
let h = host_xdg_output @@ object
|
|
inherit [_] H.Zxdg_output_v1.v1
|
|
method on_description _ = C.Zxdg_output_v1.description c
|
|
method on_done _ = C.Zxdg_output_v1.done_ c
|
|
|
|
method on_logical_position _ ~x ~y =
|
|
let (x, y) = scale_to_client ~xwayland (x, y) in
|
|
C.Zxdg_output_v1.logical_position c ~x ~y
|
|
|
|
method on_logical_size _ ~width ~height =
|
|
let (width, height) = scale_to_client ~xwayland (width, height) in
|
|
C.Zxdg_output_v1.logical_size c ~width ~height
|
|
|
|
method on_name _ = C.Zxdg_output_v1.name c
|
|
end in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zxdg_output_v1.v1
|
|
method on_destroy = delete_with H.Zxdg_output_v1.destroy h
|
|
end
|
|
|
|
let make_zxdg_output_manager_v1 ~xwayland bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Zxdg_output_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Zxdg_output_manager_v1.v1
|
|
|
|
method on_destroy = delete_with H.Zxdg_output_manager_v1.destroy h
|
|
|
|
method on_get_xdg_output _ c ~output =
|
|
let output = to_host output in
|
|
make_zxdg_output ~xwayland ~host_xdg_output:(H.Zxdg_output_manager_v1.get_xdg_output h ~output) c
|
|
end
|
|
|
|
let make_kde_decoration ~host_decoration c =
|
|
let server_mode = H.Org_kde_kwin_server_decoration.Mode.(to_int32 Server) in
|
|
let h = host_decoration @@ object
|
|
inherit [_] H.Org_kde_kwin_server_decoration.v1
|
|
method on_mode _ ~mode:_ =
|
|
(* Always tell client to use server-side decorations *)
|
|
C.Org_kde_kwin_server_decoration.mode c ~mode:server_mode
|
|
end
|
|
in
|
|
(* Request server-side from host *)
|
|
H.Org_kde_kwin_server_decoration.request_mode h ~mode:server_mode;
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Org_kde_kwin_server_decoration.v1
|
|
method on_release = delete_with H.Org_kde_kwin_server_decoration.release h
|
|
method on_request_mode _ ~mode:_ =
|
|
(* Ignore client preference, always request server-side from host *)
|
|
H.Org_kde_kwin_server_decoration.request_mode h ~mode:server_mode
|
|
end
|
|
|
|
let make_kde_decoration_manager bind c =
|
|
let h = bind @@ object
|
|
inherit [_] H.Org_kde_kwin_server_decoration_manager.v1
|
|
method on_default_mode _ = C.Org_kde_kwin_server_decoration_manager.default_mode c
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Org_kde_kwin_server_decoration_manager.v1
|
|
method on_create _ decoration ~surface =
|
|
let surface = to_host surface in
|
|
make_kde_decoration ~host_decoration:(H.Org_kde_kwin_server_decoration_manager.create h ~surface) decoration
|
|
end
|
|
|
|
let make_xdg_decoration ~decoration_info ~host_decoration c =
|
|
let h, owns_host_decoration = match decoration_info with
|
|
| Some di -> (
|
|
match di.CD.host_decoration with
|
|
| Some existing ->
|
|
(* Wire up configure callback to forward to this client *)
|
|
di.on_decoration_configure <- Some (fun () ->
|
|
C.Zxdg_toplevel_decoration_v1.configure c
|
|
~mode:C.Zxdg_toplevel_decoration_v1.Mode.Server_side
|
|
);
|
|
existing, false (* Don't destroy - owned by make_toplevel *)
|
|
| None ->
|
|
(* Host doesn't support decorations - create via manager *)
|
|
host_decoration @@ object
|
|
inherit [_] H.Zxdg_toplevel_decoration_v1.v1
|
|
method on_configure _ ~mode:_ =
|
|
C.Zxdg_toplevel_decoration_v1.configure c
|
|
~mode:C.Zxdg_toplevel_decoration_v1.Mode.Server_side
|
|
end, true
|
|
)
|
|
| None ->
|
|
(* Called without decoration_info - shouldn't happen in normal flow *)
|
|
host_decoration @@ object
|
|
inherit [_] H.Zxdg_toplevel_decoration_v1.v1
|
|
method on_configure _ ~mode:_ =
|
|
C.Zxdg_toplevel_decoration_v1.configure c
|
|
~mode:C.Zxdg_toplevel_decoration_v1.Mode.Server_side
|
|
end, true
|
|
in
|
|
(* Request server-side from host *)
|
|
H.Zxdg_toplevel_decoration_v1.set_mode h ~mode:H.Zxdg_toplevel_decoration_v1.Mode.Server_side;
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zxdg_toplevel_decoration_v1.v1
|
|
method on_destroy client_proxy =
|
|
(* Clear callback when client destroys their decoration *)
|
|
(match decoration_info with
|
|
| Some di -> di.on_decoration_configure <- None
|
|
| None -> ());
|
|
(* Only destroy host decoration if we created it *)
|
|
if owns_host_decoration then
|
|
(delete_with H.Zxdg_toplevel_decoration_v1.destroy h) client_proxy
|
|
else
|
|
Proxy.delete client_proxy
|
|
method on_set_mode _ ~mode:_ =
|
|
(* Ignore client preference, always request server-side from host *)
|
|
H.Zxdg_toplevel_decoration_v1.set_mode h ~mode:H.Zxdg_toplevel_decoration_v1.Mode.Server_side
|
|
method on_unset_mode _ =
|
|
(* Even on unset, request server-side *)
|
|
H.Zxdg_toplevel_decoration_v1.set_mode h ~mode:H.Zxdg_toplevel_decoration_v1.Mode.Server_side
|
|
end
|
|
|
|
let make_xdg_decoration_manager bind c =
|
|
let h = bind @@ object
|
|
inherit [_] H.Zxdg_decoration_manager_v1.v1
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zxdg_decoration_manager_v1.v1
|
|
method on_destroy = delete_with H.Zxdg_decoration_manager_v1.destroy h
|
|
method on_get_toplevel_decoration _ decoration ~toplevel =
|
|
let decoration_info = get_toplevel_decoration toplevel in
|
|
match decoration_info.host_decoration with
|
|
| Some _ ->
|
|
(* Reuse existing decoration created in make_toplevel *)
|
|
make_xdg_decoration ~decoration_info:(Some decoration_info)
|
|
~host_decoration:(fun _ -> assert false) decoration
|
|
| None ->
|
|
(* No proactive decoration - create one now (host doesn't support?) *)
|
|
let host_toplevel = to_host toplevel in
|
|
make_xdg_decoration ~decoration_info:None
|
|
~host_decoration:(H.Zxdg_decoration_manager_v1.get_toplevel_decoration h ~toplevel:host_toplevel)
|
|
decoration
|
|
end
|
|
|
|
let make_relative_pointer ~host_relative_pointer c =
|
|
let h =
|
|
host_relative_pointer @@ object
|
|
inherit [_] H.Zwp_relative_pointer_v1.v1
|
|
method on_relative_motion _ = C.Zwp_relative_pointer_v1.relative_motion c
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zwp_relative_pointer_v1.v1
|
|
method on_destroy = delete_with H.Zwp_relative_pointer_v1.destroy h
|
|
end
|
|
|
|
let make_relative_pointer_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Zwp_relative_pointer_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Zwp_relative_pointer_manager_v1.v1
|
|
method on_destroy = delete_with H.Zwp_relative_pointer_manager_v1.destroy h
|
|
method on_get_relative_pointer _ relative_pointer ~pointer =
|
|
let host_relative_pointer = H.Zwp_relative_pointer_manager_v1.get_relative_pointer h ~pointer:(to_host pointer) in
|
|
make_relative_pointer ~host_relative_pointer relative_pointer
|
|
end
|
|
|
|
let make_data_offer ~client_offer h =
|
|
let c = client_offer @@ object
|
|
inherit [_] C.Wl_data_offer.v1
|
|
method on_accept _ = H.Wl_data_offer.accept h
|
|
method on_destroy c =
|
|
delete_with H.Wl_data_offer.destroy h c;
|
|
(* Effectively, the "selection" event is the destructor of the previous selection,
|
|
and this is the confirmation. The server doesn't send a delete event, so just do it manually. *)
|
|
Proxy.delete h
|
|
method on_finish _ = H.Wl_data_offer.finish h
|
|
method on_receive _ ~mime_type ~fd =
|
|
H.Wl_data_offer.receive h ~mime_type ~fd;
|
|
Unix.close fd
|
|
method on_set_actions _ = H.Wl_data_offer.set_actions h
|
|
end in
|
|
let user_data = host_data (HD.Data_offer c) in
|
|
Proxy.Handler.attach h @@ object
|
|
inherit [_] H.Wl_data_offer.v1
|
|
method! user_data = user_data
|
|
method on_action _ = C.Wl_data_offer.action c
|
|
method on_offer _ = C.Wl_data_offer.offer c
|
|
method on_source_actions _ = C.Wl_data_offer.source_actions c
|
|
end
|
|
|
|
let make_data_source ~host_source c =
|
|
let c = cv c in
|
|
let h =
|
|
host_source @@ object
|
|
inherit [_] H.Wl_data_source.v1
|
|
method on_action _ = C.Wl_data_source.action c
|
|
method on_cancelled _ = C.Wl_data_source.cancelled c
|
|
method on_dnd_drop_performed _ = C.Wl_data_source.dnd_drop_performed c
|
|
method on_dnd_finished _ = C.Wl_data_source.dnd_finished c
|
|
method on_send _ ~mime_type ~fd =
|
|
C.Wl_data_source.send c ~mime_type ~fd;
|
|
Unix.close fd
|
|
method on_target _ = C.Wl_data_source.target c
|
|
end in
|
|
let user_data = client_data (Data_source h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_data_source.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Wl_data_source.destroy h
|
|
method on_offer _ = H.Wl_data_source.offer h
|
|
method on_set_actions _ = H.Wl_data_source.set_actions h
|
|
end
|
|
|
|
let make_data_device ~xwayland ~host_device c =
|
|
let c = cv c in
|
|
let h = host_device @@ object
|
|
inherit [_] H.Wl_data_device.v1
|
|
method on_data_offer _ offer = make_data_offer ~client_offer:(C.Wl_data_device.data_offer c) offer
|
|
method on_drop _ = C.Wl_data_device.drop c
|
|
|
|
method on_enter _ ~serial ~surface ~x ~y offer =
|
|
let (x, y) = point_to_client ~xwayland (x, y) in
|
|
C.Wl_data_device.enter c ~serial ~surface:(to_client surface) ~x ~y (Option.map to_client offer)
|
|
|
|
method on_leave _ = C.Wl_data_device.leave c
|
|
|
|
method on_motion _ ~time ~x ~y =
|
|
let (x, y) = point_to_client ~xwayland (x, y) in
|
|
C.Wl_data_device.motion c ~time ~x ~y
|
|
|
|
method on_selection _ offer = C.Wl_data_device.selection c (Option.map to_client offer)
|
|
end in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wl_data_device.v1
|
|
method on_release = delete_with H.Wl_data_device.release h
|
|
method on_set_selection _ ~source = H.Wl_data_device.set_selection h ~source:(Option.map to_host source)
|
|
method on_start_drag _ ~source ~origin ~icon =
|
|
H.Wl_data_device.start_drag h
|
|
~source:(Option.map to_host source)
|
|
~origin:(to_host origin)
|
|
~icon:(Option.map to_host icon)
|
|
end
|
|
|
|
let make_data_device_manager ~xwayland bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = cv @@ bind @@ new H.Wl_data_device_manager.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wl_data_device_manager.v1
|
|
method on_create_data_source _ c =
|
|
make_data_source c ~host_source:(H.Wl_data_device_manager.create_data_source h)
|
|
method on_get_data_device _ c ~seat =
|
|
let seat = to_host seat in
|
|
make_data_device ~xwayland c ~host_device:(H.Wl_data_device_manager.get_data_device h ~seat)
|
|
end
|
|
|
|
module Gtk_primary = struct
|
|
let make_gtk_data_offer ~client_offer h =
|
|
let c = client_offer @@ object
|
|
inherit [_] C.Gtk_primary_selection_offer.v1
|
|
|
|
method on_destroy c =
|
|
delete_with H.Zwp_primary_selection_offer_v1.destroy h c;
|
|
(* Effectively, the "selection" event is the destructor of the previous selection,
|
|
and this is the confirmation. The server doesn't send a delete event, so just do it manually. *)
|
|
Proxy.delete h
|
|
|
|
method on_receive _ ~mime_type ~fd =
|
|
H.Zwp_primary_selection_offer_v1.receive h ~mime_type ~fd;
|
|
Unix.close fd
|
|
end in
|
|
let user_data = host_data (HD.Gtk_data_offer c) in
|
|
Proxy.Handler.attach h @@ object
|
|
inherit [_] H.Zwp_primary_selection_offer_v1.v1
|
|
method! user_data = user_data
|
|
method on_offer _ = C.Gtk_primary_selection_offer.offer c
|
|
end
|
|
|
|
let make_gtk_primary_selection_source ~host_source c =
|
|
let h =
|
|
host_source @@ object
|
|
inherit [_] H.Zwp_primary_selection_source_v1.v1
|
|
method on_cancelled _ = C.Gtk_primary_selection_source.cancelled c
|
|
method on_send _ ~mime_type ~fd =
|
|
C.Gtk_primary_selection_source.send c ~mime_type ~fd;
|
|
Unix.close fd
|
|
end in
|
|
let user_data = client_data (Gtk_source h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Gtk_primary_selection_source.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Zwp_primary_selection_source_v1.destroy h
|
|
method on_offer _ = H.Zwp_primary_selection_source_v1.offer h
|
|
end
|
|
|
|
let make_gtk_primary_selection_device ~host_device c =
|
|
let h = host_device @@ object
|
|
inherit [_] H.Zwp_primary_selection_device_v1.v1
|
|
method on_data_offer _ offer = make_gtk_data_offer ~client_offer:(C.Gtk_primary_selection_device.data_offer c) offer
|
|
method on_selection _ offer =
|
|
let to_client x =
|
|
let Host_data data = user_data x in
|
|
match data with
|
|
| HD.Gtk_data_offer c -> cv c
|
|
| HD.Zwp_data_offer _ -> failwith "Can't mix Zwp and Gtk selection protocols!"
|
|
in
|
|
C.Gtk_primary_selection_device.selection c (Option.map to_client offer)
|
|
end in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Gtk_primary_selection_device.v1
|
|
method on_destroy = delete_with H.Zwp_primary_selection_device_v1.destroy h
|
|
method on_set_selection _ ~source =
|
|
let to_host x =
|
|
let Client_data (CD.Gtk_source data) = user_data x in
|
|
cv data
|
|
in
|
|
let source = Option.map to_host source in
|
|
H.Zwp_primary_selection_device_v1.set_selection h ~source
|
|
end
|
|
|
|
let make_device_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Zwp_primary_selection_device_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Gtk_primary_selection_device_manager.v1
|
|
method on_create_source _ source =
|
|
let host_source = H.Zwp_primary_selection_device_manager_v1.create_source h in
|
|
make_gtk_primary_selection_source ~host_source source
|
|
method on_destroy = delete_with H.Zwp_primary_selection_device_manager_v1.destroy h
|
|
method on_get_device _ dev ~seat =
|
|
let seat = to_host seat in
|
|
let host_device = H.Zwp_primary_selection_device_manager_v1.get_device h ~seat in
|
|
make_gtk_primary_selection_device ~host_device dev
|
|
end
|
|
end
|
|
|
|
let make_locked_pointer ~host_pointer c =
|
|
let h = host_pointer @@ object
|
|
inherit [_] H.Zwp_locked_pointer_v1.v1
|
|
method on_locked _ = C.Zwp_locked_pointer_v1.locked c
|
|
method on_unlocked _ = C.Zwp_locked_pointer_v1.unlocked c
|
|
end in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zwp_locked_pointer_v1.v1
|
|
method on_destroy = delete_with H.Zwp_locked_pointer_v1.destroy h
|
|
method on_set_region _ ~region = H.Zwp_locked_pointer_v1.set_region h ~region:(Option.map to_host region)
|
|
method on_set_cursor_position_hint _ ~surface_x ~surface_y =
|
|
H.Zwp_locked_pointer_v1.set_cursor_position_hint h ~surface_x:surface_x ~surface_y:surface_y
|
|
end
|
|
|
|
let make_confined_pointer ~host_pointer c =
|
|
let h = host_pointer @@ object
|
|
inherit [_] H.Zwp_confined_pointer_v1.v1
|
|
method on_confined _ = C.Zwp_confined_pointer_v1.confined c
|
|
method on_unconfined _ = C.Zwp_confined_pointer_v1.unconfined c
|
|
end in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zwp_confined_pointer_v1.v1
|
|
method on_destroy = delete_with H.Zwp_confined_pointer_v1.destroy h
|
|
method on_set_region _ ~region = H.Zwp_confined_pointer_v1.set_region h ~region:(Option.map to_host region)
|
|
end
|
|
|
|
let make_pointer_constraints bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Zwp_pointer_constraints_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Zwp_pointer_constraints_v1.v1
|
|
method on_destroy = delete_with H.Zwp_pointer_constraints_v1.destroy h
|
|
|
|
method on_lock_pointer _ locked_pointer ~surface ~pointer ~region ~lifetime =
|
|
let surface = to_host surface in
|
|
let pointer = to_host pointer in
|
|
let region = Option.map to_host region in
|
|
let host_pointer = H.Zwp_pointer_constraints_v1.lock_pointer h ~surface ~pointer ~region ~lifetime in
|
|
make_locked_pointer ~host_pointer locked_pointer
|
|
|
|
method on_confine_pointer _ locked_pointer ~surface ~pointer ~region ~lifetime =
|
|
let surface = to_host surface in
|
|
let pointer = to_host pointer in
|
|
let region = Option.map to_host region in
|
|
let host_pointer = H.Zwp_pointer_constraints_v1.confine_pointer h ~surface ~pointer ~region ~lifetime in
|
|
make_confined_pointer ~host_pointer locked_pointer
|
|
end
|
|
|
|
let make_viewport ~host_viewport c =
|
|
let h = host_viewport @@ new H.Wp_viewport.v1 in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_viewport.v1
|
|
|
|
method on_destroy = delete_with H.Wp_viewport.destroy h
|
|
method on_set_source _ = H.Wp_viewport.set_source h
|
|
method on_set_destination _ = H.Wp_viewport.set_destination h
|
|
end
|
|
|
|
let make_viewporter bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Wp_viewporter.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_viewporter.v1
|
|
|
|
method on_destroy = delete_with H.Wp_viewporter.destroy h
|
|
|
|
method on_get_viewport _ viewport ~surface =
|
|
let surface = to_host surface in
|
|
let host_viewport = H.Wp_viewporter.get_viewport h ~surface in
|
|
make_viewport ~host_viewport viewport
|
|
end
|
|
|
|
let make_fractional_scale ~host_fractional_scale c =
|
|
let h = host_fractional_scale @@ object
|
|
inherit [_] H.Wp_fractional_scale_v1.v1
|
|
method on_preferred_scale _ ~scale =
|
|
C.Wp_fractional_scale_v1.preferred_scale c ~scale
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_fractional_scale_v1.v1
|
|
method on_destroy = delete_with H.Wp_fractional_scale_v1.destroy h
|
|
end
|
|
|
|
let make_fractional_scale_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Wp_fractional_scale_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_fractional_scale_manager_v1.v1
|
|
method on_destroy = delete_with H.Wp_fractional_scale_manager_v1.destroy h
|
|
method on_get_fractional_scale _ fractional_scale ~surface =
|
|
let surface = to_host surface in
|
|
let host_fs = H.Wp_fractional_scale_manager_v1.get_fractional_scale h ~surface in
|
|
make_fractional_scale ~host_fractional_scale:host_fs fractional_scale
|
|
end
|
|
|
|
let make_single_pixel_buffer_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Wp_single_pixel_buffer_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_single_pixel_buffer_manager_v1.v1
|
|
method on_destroy = delete_with H.Wp_single_pixel_buffer_manager_v1.destroy h
|
|
method on_create_u32_rgba_buffer _ buffer ~r ~g ~b ~a =
|
|
let host_buffer =
|
|
H.Wp_single_pixel_buffer_manager_v1.create_u32_rgba_buffer h ~r ~g ~b ~a @@ object
|
|
inherit [_] H.Wl_buffer.v1
|
|
method on_release _ = C.Wl_buffer.release buffer
|
|
end
|
|
in
|
|
Proxy.Handler.attach buffer @@ object
|
|
inherit [_] C.Wl_buffer.v1
|
|
method! user_data = client_data (Buffer (`Direct host_buffer))
|
|
method on_destroy = delete_with H.Wl_buffer.destroy host_buffer
|
|
end
|
|
end
|
|
|
|
let make_cursor_dev ~host_dev c =
|
|
let h = host_dev @@ new H.Wp_cursor_shape_device_v1.v1 in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_cursor_shape_device_v1.v1
|
|
|
|
method on_destroy = delete_with H.Wp_cursor_shape_device_v1.destroy h
|
|
method on_set_shape _ = H.Wp_cursor_shape_device_v1.set_shape h
|
|
end
|
|
|
|
let make_cursor_shape_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Wp_cursor_shape_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_cursor_shape_manager_v1.v1
|
|
|
|
method on_destroy = delete_with H.Wp_cursor_shape_manager_v1.destroy h
|
|
|
|
method on_get_tablet_tool_v2 _ dev ~tablet_tool =
|
|
let tablet_tool = to_host tablet_tool in
|
|
let host_dev = H.Wp_cursor_shape_manager_v1.get_tablet_tool_v2 h ~tablet_tool in
|
|
make_cursor_dev ~host_dev dev
|
|
|
|
method on_get_pointer _ dev ~pointer =
|
|
let pointer = to_host pointer in
|
|
let host_dev = H.Wp_cursor_shape_manager_v1.get_pointer h ~pointer in
|
|
make_cursor_dev ~host_dev dev
|
|
end
|
|
|
|
(* This is basically the same as [Gtk_primary], but with things renamed a bit. *)
|
|
module Zwp_primary = struct
|
|
let make_data_offer ~client_offer h =
|
|
let c = client_offer @@ object
|
|
inherit [_] C.Zwp_primary_selection_offer_v1.v1
|
|
|
|
method on_destroy c =
|
|
delete_with H.Zwp_primary_selection_offer_v1.destroy h c;
|
|
(* Effectively, the "selection" event is the destructor of the previous selection,
|
|
and this is the confirmation. The server doesn't send a delete event, so just do it manually. *)
|
|
Proxy.delete h
|
|
|
|
method on_receive _ ~mime_type ~fd =
|
|
H.Zwp_primary_selection_offer_v1.receive h ~mime_type ~fd;
|
|
Unix.close fd
|
|
end in
|
|
let user_data = host_data (HD.Zwp_data_offer c) in
|
|
Proxy.Handler.attach h @@ object
|
|
inherit [_] H.Zwp_primary_selection_offer_v1.v1
|
|
method! user_data = user_data
|
|
method on_offer _ = C.Zwp_primary_selection_offer_v1.offer c
|
|
end
|
|
|
|
let make_primary_selection_source ~host_source c =
|
|
let h =
|
|
host_source @@ object
|
|
inherit [_] H.Zwp_primary_selection_source_v1.v1
|
|
method on_cancelled _ = C.Zwp_primary_selection_source_v1.cancelled c
|
|
method on_send _ ~mime_type ~fd =
|
|
C.Zwp_primary_selection_source_v1.send c ~mime_type ~fd;
|
|
Unix.close fd
|
|
end in
|
|
let user_data = client_data (Zwp_source h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zwp_primary_selection_source_v1.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Zwp_primary_selection_source_v1.destroy h
|
|
method on_offer _ = H.Zwp_primary_selection_source_v1.offer h
|
|
end
|
|
|
|
let make_primary_selection_device ~host_device c =
|
|
let h = host_device @@ object
|
|
inherit [_] H.Zwp_primary_selection_device_v1.v1
|
|
method on_data_offer _ offer = make_data_offer ~client_offer:(C.Zwp_primary_selection_device_v1.data_offer c) offer
|
|
method on_selection _ offer = C.Zwp_primary_selection_device_v1.selection c (Option.map to_client offer)
|
|
end in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Zwp_primary_selection_device_v1.v1
|
|
method on_destroy = delete_with H.Zwp_primary_selection_device_v1.destroy h
|
|
method on_set_selection _ ~source =
|
|
let source = Option.map to_host source in
|
|
H.Zwp_primary_selection_device_v1.set_selection h ~source
|
|
end
|
|
|
|
let make_device_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Zwp_primary_selection_device_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Zwp_primary_selection_device_manager_v1.v1
|
|
method on_create_source _ source =
|
|
let host_source = H.Zwp_primary_selection_device_manager_v1.create_source h in
|
|
make_primary_selection_source ~host_source source
|
|
method on_destroy = delete_with H.Zwp_primary_selection_device_manager_v1.destroy h
|
|
method on_get_device _ dev ~seat =
|
|
let seat = to_host seat in
|
|
let host_device = H.Zwp_primary_selection_device_manager_v1.get_device h ~seat in
|
|
make_primary_selection_device ~host_device dev
|
|
end
|
|
end
|
|
|
|
(* -- alpha-modifier-v1 -- *)
|
|
|
|
let make_alpha_modifier_surface ~host_alpha_modifier_surface c =
|
|
let h = host_alpha_modifier_surface @@ new H.Wp_alpha_modifier_surface_v1.v1 in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_alpha_modifier_surface_v1.v1
|
|
method on_destroy = delete_with H.Wp_alpha_modifier_surface_v1.destroy h
|
|
method on_set_multiplier _ = H.Wp_alpha_modifier_surface_v1.set_multiplier h
|
|
end
|
|
|
|
let make_alpha_modifier bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Wp_alpha_modifier_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_alpha_modifier_v1.v1
|
|
method on_destroy = delete_with H.Wp_alpha_modifier_v1.destroy h
|
|
method on_get_surface _ alpha_modifier_surface ~surface =
|
|
let surface = to_host surface in
|
|
let host_ams = H.Wp_alpha_modifier_v1.get_surface h ~surface in
|
|
make_alpha_modifier_surface ~host_alpha_modifier_surface:host_ams alpha_modifier_surface
|
|
end
|
|
|
|
(* -- color-representation-v1 -- *)
|
|
|
|
let make_color_representation_surface ~host_cr_surface c =
|
|
let h = host_cr_surface @@ new H.Wp_color_representation_surface_v1.v1 in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_color_representation_surface_v1.v1
|
|
method on_destroy = delete_with H.Wp_color_representation_surface_v1.destroy h
|
|
method on_set_alpha_mode _ = H.Wp_color_representation_surface_v1.set_alpha_mode h
|
|
method on_set_coefficients_and_range _ = H.Wp_color_representation_surface_v1.set_coefficients_and_range h
|
|
method on_set_chroma_location _ = H.Wp_color_representation_surface_v1.set_chroma_location h
|
|
end
|
|
|
|
let make_color_representation_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ object
|
|
inherit [_] H.Wp_color_representation_manager_v1.v1
|
|
method on_supported_alpha_mode _ ~alpha_mode =
|
|
C.Wp_color_representation_manager_v1.supported_alpha_mode proxy ~alpha_mode
|
|
method on_supported_coefficients_and_ranges _ ~coefficients ~range =
|
|
C.Wp_color_representation_manager_v1.supported_coefficients_and_ranges proxy ~coefficients ~range
|
|
method on_done _ = C.Wp_color_representation_manager_v1.done_ proxy
|
|
end
|
|
in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_color_representation_manager_v1.v1
|
|
method on_destroy = delete_with H.Wp_color_representation_manager_v1.destroy h
|
|
method on_get_surface _ cr_surface ~surface =
|
|
let surface = to_host surface in
|
|
let host_crs = H.Wp_color_representation_manager_v1.get_surface h ~surface in
|
|
make_color_representation_surface ~host_cr_surface:host_crs cr_surface
|
|
end
|
|
|
|
(* -- color-management-v1 -- *)
|
|
|
|
let make_image_description_info ~host_info c =
|
|
let h = host_info @@ object
|
|
inherit [_] H.Wp_image_description_info_v1.v1
|
|
method on_done = C.Wp_image_description_info_v1.done_ c
|
|
method on_icc_file _ ~icc ~icc_size =
|
|
C.Wp_image_description_info_v1.icc_file c ~icc ~icc_size;
|
|
Unix.close icc
|
|
method on_primaries _ = C.Wp_image_description_info_v1.primaries c
|
|
method on_primaries_named _ = C.Wp_image_description_info_v1.primaries_named c
|
|
method on_tf_power _ = C.Wp_image_description_info_v1.tf_power c
|
|
method on_tf_named _ = C.Wp_image_description_info_v1.tf_named c
|
|
method on_luminances _ = C.Wp_image_description_info_v1.luminances c
|
|
method on_target_primaries _ = C.Wp_image_description_info_v1.target_primaries c
|
|
method on_target_luminance _ = C.Wp_image_description_info_v1.target_luminance c
|
|
method on_target_max_cll _ = C.Wp_image_description_info_v1.target_max_cll c
|
|
method on_target_max_fall _ = C.Wp_image_description_info_v1.target_max_fall c
|
|
end
|
|
in
|
|
Proxy.on_delete h (fun () -> if Proxy.transport_up c then Proxy.delete c);
|
|
Proxy.Handler.attach c @@ new C.Wp_image_description_info_v1.v1
|
|
|
|
let make_image_description ~host_image_desc c =
|
|
let h = host_image_desc @@ object
|
|
inherit [_] H.Wp_image_description_v1.v1
|
|
method on_failed _ = C.Wp_image_description_v1.failed c
|
|
method on_ready _ = C.Wp_image_description_v1.ready c
|
|
method on_ready2 _ = C.Wp_image_description_v1.ready2 c
|
|
end
|
|
in
|
|
let user_data = client_data (Image_description h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_image_description_v1.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Wp_image_description_v1.destroy h
|
|
method on_get_information _ info =
|
|
let host_info = H.Wp_image_description_v1.get_information h in
|
|
make_image_description_info ~host_info info
|
|
end
|
|
|
|
let make_image_description_creator_icc ~host_creator c =
|
|
let h = host_creator @@ new H.Wp_image_description_creator_icc_v1.v1 in
|
|
Proxy.on_delete h (fun () -> if Proxy.transport_up c then Proxy.delete c);
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_image_description_creator_icc_v1.v1
|
|
method on_set_icc_file _ ~icc_profile ~offset ~length =
|
|
H.Wp_image_description_creator_icc_v1.set_icc_file h ~icc_profile ~offset ~length;
|
|
Unix.close icc_profile
|
|
method on_create _ image_description =
|
|
let host_image_desc = H.Wp_image_description_creator_icc_v1.create h in
|
|
make_image_description ~host_image_desc image_description
|
|
end
|
|
|
|
let make_image_description_creator_params ~host_creator c =
|
|
let h = host_creator @@ new H.Wp_image_description_creator_params_v1.v1 in
|
|
Proxy.on_delete h (fun () -> if Proxy.transport_up c then Proxy.delete c);
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_image_description_creator_params_v1.v1
|
|
method on_set_tf_named _ = H.Wp_image_description_creator_params_v1.set_tf_named h
|
|
method on_set_tf_power _ = H.Wp_image_description_creator_params_v1.set_tf_power h
|
|
method on_set_primaries_named _ = H.Wp_image_description_creator_params_v1.set_primaries_named h
|
|
method on_set_primaries _ = H.Wp_image_description_creator_params_v1.set_primaries h
|
|
method on_set_luminances _ = H.Wp_image_description_creator_params_v1.set_luminances h
|
|
method on_set_mastering_display_primaries _ = H.Wp_image_description_creator_params_v1.set_mastering_display_primaries h
|
|
method on_set_mastering_luminance _ = H.Wp_image_description_creator_params_v1.set_mastering_luminance h
|
|
method on_set_max_cll _ = H.Wp_image_description_creator_params_v1.set_max_cll h
|
|
method on_set_max_fall _ = H.Wp_image_description_creator_params_v1.set_max_fall h
|
|
method on_create _ image_description =
|
|
let host_image_desc = H.Wp_image_description_creator_params_v1.create h in
|
|
make_image_description ~host_image_desc image_description
|
|
end
|
|
|
|
(* Reference objects are created by external protocols. This handler
|
|
exists so that to_host works when those protocols are added. *)
|
|
let _make_image_description_reference ~host_ref c =
|
|
let h = host_ref @@ new H.Wp_image_description_reference_v1.v1 in
|
|
let user_data = client_data (Image_desc_reference h) in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_image_description_reference_v1.v1
|
|
method! user_data = user_data
|
|
method on_destroy = delete_with H.Wp_image_description_reference_v1.destroy h
|
|
end
|
|
|
|
let make_color_management_output ~host_cm_output c =
|
|
let h = host_cm_output @@ object
|
|
inherit [_] H.Wp_color_management_output_v1.v1
|
|
method on_image_description_changed _ =
|
|
C.Wp_color_management_output_v1.image_description_changed c
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_color_management_output_v1.v1
|
|
method on_destroy = delete_with H.Wp_color_management_output_v1.destroy h
|
|
method on_get_image_description _ image_description =
|
|
let host_image_desc = H.Wp_color_management_output_v1.get_image_description h in
|
|
make_image_description ~host_image_desc image_description
|
|
end
|
|
|
|
let make_color_management_surface ~host_cm_surface c =
|
|
let h = host_cm_surface @@ new H.Wp_color_management_surface_v1.v1 in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_color_management_surface_v1.v1
|
|
method on_destroy = delete_with H.Wp_color_management_surface_v1.destroy h
|
|
method on_set_image_description _ ~image_description ~render_intent =
|
|
let image_description = to_host image_description in
|
|
H.Wp_color_management_surface_v1.set_image_description h ~image_description ~render_intent
|
|
method on_unset_image_description _ =
|
|
H.Wp_color_management_surface_v1.unset_image_description h
|
|
end
|
|
|
|
let make_color_management_surface_feedback ~host_cm_feedback c =
|
|
let h = host_cm_feedback @@ object
|
|
inherit [_] H.Wp_color_management_surface_feedback_v1.v1
|
|
method on_preferred_changed _ = C.Wp_color_management_surface_feedback_v1.preferred_changed c
|
|
method on_preferred_changed2 _ = C.Wp_color_management_surface_feedback_v1.preferred_changed2 c
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_color_management_surface_feedback_v1.v1
|
|
method on_destroy = delete_with H.Wp_color_management_surface_feedback_v1.destroy h
|
|
method on_get_preferred _ image_description =
|
|
let host_image_desc = H.Wp_color_management_surface_feedback_v1.get_preferred h in
|
|
make_image_description ~host_image_desc image_description
|
|
method on_get_preferred_parametric _ image_description =
|
|
let host_image_desc = H.Wp_color_management_surface_feedback_v1.get_preferred_parametric h in
|
|
make_image_description ~host_image_desc image_description
|
|
end
|
|
|
|
let make_color_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ object
|
|
inherit [_] H.Wp_color_manager_v1.v1
|
|
method on_supported_intent _ = C.Wp_color_manager_v1.supported_intent proxy
|
|
method on_supported_feature _ = C.Wp_color_manager_v1.supported_feature proxy
|
|
method on_supported_tf_named _ = C.Wp_color_manager_v1.supported_tf_named proxy
|
|
method on_supported_primaries_named _ = C.Wp_color_manager_v1.supported_primaries_named proxy
|
|
method on_done _ = C.Wp_color_manager_v1.done_ proxy
|
|
end
|
|
in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_color_manager_v1.v1
|
|
method on_destroy = delete_with H.Wp_color_manager_v1.destroy h
|
|
method on_get_output _ cm_output ~output =
|
|
let output = to_host output in
|
|
let host_cm_output = H.Wp_color_manager_v1.get_output h ~output in
|
|
make_color_management_output ~host_cm_output cm_output
|
|
method on_get_surface _ cm_surface ~surface =
|
|
let surface = to_host surface in
|
|
let host_cm_surface = H.Wp_color_manager_v1.get_surface h ~surface in
|
|
make_color_management_surface ~host_cm_surface cm_surface
|
|
method on_get_surface_feedback _ cm_feedback ~surface =
|
|
let surface = to_host surface in
|
|
let host_cm_feedback = H.Wp_color_manager_v1.get_surface_feedback h ~surface in
|
|
make_color_management_surface_feedback ~host_cm_feedback cm_feedback
|
|
method on_create_icc_creator _ creator =
|
|
let host_creator = H.Wp_color_manager_v1.create_icc_creator h in
|
|
make_image_description_creator_icc ~host_creator creator
|
|
method on_create_parametric_creator _ creator =
|
|
let host_creator = H.Wp_color_manager_v1.create_parametric_creator h in
|
|
make_image_description_creator_params ~host_creator creator
|
|
method on_create_windows_scrgb _ image_description =
|
|
let host_image_desc = H.Wp_color_manager_v1.create_windows_scrgb h in
|
|
make_image_description ~host_image_desc image_description
|
|
method on_get_image_description _ image_description ~reference =
|
|
let reference = to_host reference in
|
|
let host_image_desc = H.Wp_color_manager_v1.get_image_description h ~reference in
|
|
make_image_description ~host_image_desc image_description
|
|
end
|
|
|
|
(* -- frog-color-management-v1 -- *)
|
|
|
|
let make_frog_color_managed_surface ~host_frog_cms c =
|
|
let h = host_frog_cms @@ object
|
|
inherit [_] H.Frog_color_managed_surface.v1
|
|
method on_preferred_metadata _ ~transfer_function
|
|
~output_display_primary_red_x ~output_display_primary_red_y
|
|
~output_display_primary_green_x ~output_display_primary_green_y
|
|
~output_display_primary_blue_x ~output_display_primary_blue_y
|
|
~output_white_point_x ~output_white_point_y
|
|
~max_luminance ~min_luminance ~max_full_frame_luminance =
|
|
C.Frog_color_managed_surface.preferred_metadata c
|
|
~transfer_function
|
|
~output_display_primary_red_x ~output_display_primary_red_y
|
|
~output_display_primary_green_x ~output_display_primary_green_y
|
|
~output_display_primary_blue_x ~output_display_primary_blue_y
|
|
~output_white_point_x ~output_white_point_y
|
|
~max_luminance ~min_luminance ~max_full_frame_luminance
|
|
end
|
|
in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Frog_color_managed_surface.v1
|
|
method on_destroy = delete_with H.Frog_color_managed_surface.destroy h
|
|
method on_set_known_transfer_function _ ~transfer_function =
|
|
H.Frog_color_managed_surface.set_known_transfer_function h ~transfer_function
|
|
method on_set_known_container_color_volume _ ~primaries =
|
|
H.Frog_color_managed_surface.set_known_container_color_volume h ~primaries
|
|
method on_set_render_intent _ ~render_intent =
|
|
H.Frog_color_managed_surface.set_render_intent h ~render_intent
|
|
method on_set_hdr_metadata _
|
|
~mastering_display_primary_red_x ~mastering_display_primary_red_y
|
|
~mastering_display_primary_green_x ~mastering_display_primary_green_y
|
|
~mastering_display_primary_blue_x ~mastering_display_primary_blue_y
|
|
~mastering_white_point_x ~mastering_white_point_y
|
|
~max_display_mastering_luminance ~min_display_mastering_luminance
|
|
~max_cll ~max_fall =
|
|
H.Frog_color_managed_surface.set_hdr_metadata h
|
|
~mastering_display_primary_red_x ~mastering_display_primary_red_y
|
|
~mastering_display_primary_green_x ~mastering_display_primary_green_y
|
|
~mastering_display_primary_blue_x ~mastering_display_primary_blue_y
|
|
~mastering_white_point_x ~mastering_white_point_y
|
|
~max_display_mastering_luminance ~min_display_mastering_luminance
|
|
~max_cll ~max_fall
|
|
end
|
|
|
|
let make_frog_color_management_factory bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Frog_color_management_factory_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Frog_color_management_factory_v1.v1
|
|
method on_destroy = delete_with H.Frog_color_management_factory_v1.destroy h
|
|
method on_get_color_managed_surface _ ~surface callback =
|
|
let surface = to_host surface in
|
|
let host_cms =
|
|
H.Frog_color_management_factory_v1.get_color_managed_surface h ~surface
|
|
in
|
|
make_frog_color_managed_surface ~host_frog_cms:host_cms callback
|
|
end
|
|
|
|
(* -- fifo-v1 -- *)
|
|
|
|
let make_fifo ~host_fifo c =
|
|
let h = host_fifo @@ new H.Wp_fifo_v1.v1 in
|
|
Proxy.Handler.attach c @@ object
|
|
inherit [_] C.Wp_fifo_v1.v1
|
|
method on_set_barrier _ = H.Wp_fifo_v1.set_barrier h
|
|
method on_wait_barrier _ = H.Wp_fifo_v1.wait_barrier h
|
|
method on_destroy = delete_with H.Wp_fifo_v1.destroy h
|
|
end
|
|
|
|
let make_fifo_manager bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ new H.Wp_fifo_manager_v1.v1 in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_fifo_manager_v1.v1
|
|
method on_destroy = delete_with H.Wp_fifo_manager_v1.destroy h
|
|
method on_get_fifo _ fifo ~surface =
|
|
let surface = to_host surface in
|
|
let host_fifo = H.Wp_fifo_manager_v1.get_fifo h ~surface in
|
|
make_fifo ~host_fifo fifo
|
|
end
|
|
|
|
(* -- presentation-time -- *)
|
|
|
|
let make_presentation_feedback ~host_feedback c =
|
|
Proxy.Handler.attach c @@ new C.Wp_presentation_feedback.v1;
|
|
let h = host_feedback @@ object
|
|
inherit [_] H.Wp_presentation_feedback.v1
|
|
method on_sync_output _ ~output =
|
|
let output = to_client output in
|
|
C.Wp_presentation_feedback.sync_output c ~output
|
|
method on_presented ~tv_sec_hi ~tv_sec_lo ~tv_nsec
|
|
~refresh ~seq_hi ~seq_lo ~flags =
|
|
C.Wp_presentation_feedback.presented c
|
|
~tv_sec_hi ~tv_sec_lo ~tv_nsec ~refresh ~seq_hi ~seq_lo ~flags
|
|
method on_discarded =
|
|
C.Wp_presentation_feedback.discarded c
|
|
end
|
|
in
|
|
Proxy.on_delete h (fun () -> if Proxy.transport_up c then Proxy.delete c)
|
|
|
|
let make_presentation bind proxy =
|
|
let proxy = Proxy.cast_version proxy in
|
|
let h = bind @@ object
|
|
inherit [_] H.Wp_presentation.v1
|
|
method on_clock_id _ ~clk_id =
|
|
C.Wp_presentation.clock_id proxy ~clk_id
|
|
end
|
|
in
|
|
Proxy.Handler.attach proxy @@ object
|
|
inherit [_] C.Wp_presentation.v1
|
|
method on_destroy = delete_with H.Wp_presentation.destroy h
|
|
method on_feedback _ ~surface callback =
|
|
let surface = to_host surface in
|
|
let host_fb = H.Wp_presentation.feedback h ~surface in
|
|
make_presentation_feedback ~host_feedback:host_fb callback
|
|
end
|
|
|
|
type entry = Entry : int32 * (module Metadata.S) -> entry
|
|
|
|
let registry =
|
|
let open Protocols in
|
|
[
|
|
(module Wl_shm : Metadata.S);
|
|
(module Wl_compositor);
|
|
(module Wl_subcompositor);
|
|
(module Xdg_wm_base);
|
|
(module Wl_data_device_manager);
|
|
(module Zxdg_output_manager_v1);
|
|
(module Zwp_primary_selection_device_manager_v1);
|
|
(module Wl_seat); (* Must come after primary selection device, or evince crashes *)
|
|
(module Wl_output);
|
|
(module Org_kde_kwin_server_decoration_manager);
|
|
(module Zxdg_decoration_manager_v1);
|
|
(module Zwp_relative_pointer_manager_v1);
|
|
(module Zwp_pointer_constraints_v1);
|
|
(module Wp_viewporter);
|
|
(module Wp_cursor_shape_manager_v1);
|
|
(module Wp_fractional_scale_manager_v1);
|
|
(module Wp_single_pixel_buffer_manager_v1);
|
|
(module Wp_alpha_modifier_v1);
|
|
(module Wp_color_representation_manager_v1);
|
|
(module Wp_color_manager_v1);
|
|
(module Frog_color_management_factory_v1);
|
|
(module Wp_fifo_manager_v1);
|
|
(module Wp_presentation);
|
|
]
|
|
|
|
let make_registry ~xwayland t reg =
|
|
let registry =
|
|
registry |> List.concat_map (fun (module M : Metadata.S) ->
|
|
match Registry.get t.host.registry M.interface with
|
|
| [] ->
|
|
Log.info (fun f -> f "Host doesn't support service %s, so skipping" M.interface);
|
|
[]
|
|
| { Registry.name; version = host_version } :: _ ->
|
|
let max_version = min M.version host_version in
|
|
let item = (name, Entry (max_version, (module M))) in
|
|
if M.interface = Protocols.Zwp_primary_selection_device_manager_v1.interface then (
|
|
let compat = (name, Entry (max_version, (module Protocols.Gtk_primary_selection_device_manager))) in
|
|
[item; compat]
|
|
) else (
|
|
[item]
|
|
)
|
|
)
|
|
|> Array.of_list
|
|
in
|
|
Proxy.Handler.attach reg @@ object
|
|
inherit [_] C.Wl_registry.v1
|
|
|
|
method on_bind : type a. _ -> name:int32 -> (a, [`Unknown], _) Proxy.t -> unit =
|
|
fun _ ~name proxy ->
|
|
let name = Int32.to_int name in
|
|
if name < 0 || name >= Array.length registry then Fmt.failwith "Bad registry entry name %d" name;
|
|
let host_name, Entry (max_version, (module M)) = registry.(name) in
|
|
let requested_version = Proxy.version proxy in
|
|
if requested_version > max_version then
|
|
Fmt.failwith "Client asked for %S v%lu, but we only support up to %lu" M.interface requested_version max_version;
|
|
let client_interface = Proxy.interface proxy in
|
|
if client_interface <> M.interface then
|
|
Fmt.failwith "Entry %d has type %S, client expected %S!" name M.interface client_interface;
|
|
let bind x =
|
|
assert (x#min_version = 1l);
|
|
H.Wl_registry.bind (Registry.wl_registry t.host.registry) ~name:host_name (x, Proxy.version proxy)
|
|
in
|
|
let open Protocols in
|
|
let proxy = Proxy.cast_version proxy in
|
|
match Proxy.ty proxy with
|
|
| Wl_compositor.T -> make_compositor ~xwayland bind proxy
|
|
| Wl_subcompositor.T -> make_subcompositor ~xwayland bind proxy
|
|
| Wl_shm.T -> make_shm ~virtio_gpu:t.host.virtio_gpu bind proxy
|
|
| Wl_seat.T -> make_seat ~xwayland t bind proxy
|
|
| Wl_output.T -> make_output ~xwayland bind proxy
|
|
| Wl_data_device_manager.T -> make_data_device_manager ~xwayland bind proxy
|
|
| Gtk_primary_selection_device_manager.T -> Gtk_primary.make_device_manager bind proxy
|
|
| Zwp_primary_selection_device_manager_v1.T -> Zwp_primary.make_device_manager bind proxy
|
|
| Xdg_wm_base.T -> make_xdg_wm_base ~registry:t.host.registry ~xwayland ~tag:t.config.tag bind proxy
|
|
| Zxdg_output_manager_v1.T -> make_zxdg_output_manager_v1 ~xwayland bind proxy
|
|
| Org_kde_kwin_server_decoration_manager.T -> make_kde_decoration_manager bind proxy
|
|
| Zxdg_decoration_manager_v1.T -> make_xdg_decoration_manager bind proxy
|
|
| Zwp_relative_pointer_manager_v1.T -> make_relative_pointer_manager bind proxy
|
|
| Zwp_pointer_constraints_v1.T -> make_pointer_constraints bind proxy
|
|
| Wp_viewporter.T -> make_viewporter bind proxy
|
|
| Wp_cursor_shape_manager_v1.T -> make_cursor_shape_manager bind proxy
|
|
| Wp_fractional_scale_manager_v1.T -> make_fractional_scale_manager bind proxy
|
|
| Wp_single_pixel_buffer_manager_v1.T -> make_single_pixel_buffer_manager bind proxy
|
|
| Wp_alpha_modifier_v1.T -> make_alpha_modifier bind proxy
|
|
| Wp_color_representation_manager_v1.T -> make_color_representation_manager bind proxy
|
|
| Wp_color_manager_v1.T -> make_color_manager bind proxy
|
|
| Frog_color_management_factory_v1.T -> make_frog_color_management_factory bind proxy
|
|
| Wp_fifo_manager_v1.T -> make_fifo_manager bind proxy
|
|
| Wp_presentation.T -> make_presentation bind proxy
|
|
| _ -> Fmt.failwith "Invalid service name for %a" Proxy.pp proxy
|
|
end;
|
|
registry |> Array.iteri (fun name (_, entry) ->
|
|
let Entry (version, (module M)) = entry in
|
|
C.Wl_registry.global reg ~name:(Int32.of_int name) ~interface:M.interface ~version
|
|
)
|
|
|
|
let run ?xwayland ~config host client =
|
|
let t = { host; config } in
|
|
let client_transport = Wayland.Unix_transport.of_socket client in
|
|
Switch.run (fun sw ->
|
|
let s =
|
|
Server.connect ~sw client_transport ~trace:(module Trace.Client) @@ object
|
|
inherit [_] C.Wl_display.v1
|
|
method on_get_registry _ ref = make_registry ~xwayland t ref
|
|
method on_sync _ cb =
|
|
Proxy.Handler.attach cb @@ new C.Wl_callback.v1;
|
|
let h : _ Proxy.t = H.Wl_display.sync (Client.wl_display host.display) @@ object
|
|
inherit [_] H.Wl_callback.v1
|
|
method on_done ~callback_data =
|
|
C.Wl_callback.done_ cb ~callback_data
|
|
end
|
|
in
|
|
Proxy.on_delete h (fun () -> Proxy.delete cb)
|
|
end
|
|
in
|
|
ignore (s : Server.t)
|
|
);
|
|
Log.info (fun f -> f "Client finished; closing host connection");
|
|
Client.stop host.display
|