commit
6d53770d65
@ -0,0 +1,49 @@
|
||||
.PHONY: all clean
|
||||
|
||||
CC = gcc
|
||||
CLIBS = -lwayland-client
|
||||
CFLAGS = $(CLIBS)
|
||||
|
||||
EXT_SOURCE_PROTOCOL_IMPL = ext-image-capture-source-v1-protocol.c
|
||||
EXT_SOURCE_PROTOCOL_HEADER = ext-image-capture-source-v1-protocol.h
|
||||
|
||||
EXT_CAPTURE_PROTOCOL_IMPL = ext-image-copy-capture-v1-protocol.c
|
||||
EXT_CAPTURE_PROTOCOL_HEADER = ext-image-copy-capture-v1-protocol.h
|
||||
|
||||
EXT_TOPLEVEL_PROTOCOL_IMPL = ext-foreign-toplevel-list-v1-protocol.c
|
||||
EXT_TOPLEVEL_PROTOCOL_HEADER = ext-foreign-toplevel-list-v1-protocol.h
|
||||
|
||||
SRC = main.c
|
||||
OUT = screencap
|
||||
|
||||
PROTOCOL_IMPLS = $(EXT_TOPLEVEL_PROTOCOL_IMPL) $(EXT_SOURCE_PROTOCOL_IMPL) $(EXT_CAPTURE_PROTOCOL_IMPL)
|
||||
PROTOCOL_HEADERS = $(EXT_TOPLEVEL_PROTOCOL_HEADER) $(EXT_SOURCE_PROTOCOL_HEADER) $(EXT_CAPTURE_PROTOCOL_HEADER)
|
||||
PROTOCOLS = $(PROTOCOL_IMPLS) $(PROTOCOL_HEADERS)
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
clean:
|
||||
rm -f $(OUT)
|
||||
rm -f $(PROTOCOLS)
|
||||
|
||||
$(OUT): $(PROTOCOLS) $(SRC)
|
||||
$(CC) $(PROTOCOL_IMPLS) $(SRC) $(CFLAGS) -o $(OUT)
|
||||
|
||||
$(EXT_SOURCE_PROTOCOL_IMPL):
|
||||
wayland-scanner private-code ./ext-image-capture-source-v1.xml $(EXT_SOURCE_PROTOCOL_IMPL)
|
||||
|
||||
$(EXT_SOURCE_PROTOCOL_HEADER):
|
||||
wayland-scanner client-header ./ext-image-capture-source-v1.xml $(EXT_SOURCE_PROTOCOL_HEADER)
|
||||
|
||||
$(EXT_CAPTURE_PROTOCOL_IMPL):
|
||||
wayland-scanner private-code ./ext-image-copy-capture-v1.xml $(EXT_CAPTURE_PROTOCOL_IMPL)
|
||||
|
||||
$(EXT_CAPTURE_PROTOCOL_HEADER):
|
||||
wayland-scanner client-header ./ext-image-copy-capture-v1.xml $(EXT_CAPTURE_PROTOCOL_HEADER)
|
||||
|
||||
$(EXT_TOPLEVEL_PROTOCOL_IMPL):
|
||||
wayland-scanner private-code ./ext-foreign-toplevel-list-v1.xml $(EXT_TOPLEVEL_PROTOCOL_IMPL)
|
||||
|
||||
$(EXT_TOPLEVEL_PROTOCOL_HEADER):
|
||||
wayland-scanner client-header ./ext-foreign-toplevel-list-v1.xml $(EXT_TOPLEVEL_PROTOCOL_HEADER)
|
||||
|
@ -0,0 +1,11 @@
|
||||
A simple Wayland screenshotting utility that utilizes the new `ext-image-capture-source-v1` & `ext-image-copy-capture-v1`.
|
||||
|
||||
Runtime Dependencies:
|
||||
|
||||
- `libwayland-client`
|
||||
|
||||
Make Dependencies:
|
||||
|
||||
- `wayland-scanner`
|
||||
- `make`
|
||||
- `gcc`
|
@ -0,0 +1,219 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="ext_foreign_toplevel_list_v1">
|
||||
<copyright>
|
||||
Copyright © 2018 Ilia Bozhinov
|
||||
Copyright © 2020 Isaac Freund
|
||||
Copyright © 2022 wb9688
|
||||
Copyright © 2023 i509VCB
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<description summary="list toplevels">
|
||||
The purpose of this protocol is to provide protocol object handles for
|
||||
toplevels, possibly originating from another client.
|
||||
|
||||
This protocol is intentionally minimalistic and expects additional
|
||||
functionality (e.g. creating a screencopy source from a toplevel handle,
|
||||
getting information about the state of the toplevel) to be implemented
|
||||
in extension protocols.
|
||||
|
||||
The compositor may choose to restrict this protocol to a special client
|
||||
launched by the compositor itself or expose it to all clients,
|
||||
this is compositor policy.
|
||||
|
||||
The key words "must", "must not", "required", "shall", "shall not",
|
||||
"should", "should not", "recommended", "may", and "optional" in this
|
||||
document are to be interpreted as described in IETF RFC 2119.
|
||||
|
||||
Warning! The protocol described in this file is currently in the testing
|
||||
phase. Backward compatible changes may be added together with the
|
||||
corresponding interface version bump. Backward incompatible changes can
|
||||
only be done by creating a new major version of the extension.
|
||||
</description>
|
||||
|
||||
<interface name="ext_foreign_toplevel_list_v1" version="1">
|
||||
<description summary="list toplevels">
|
||||
A toplevel is defined as a surface with a role similar to xdg_toplevel.
|
||||
XWayland surfaces may be treated like toplevels in this protocol.
|
||||
|
||||
After a client binds the ext_foreign_toplevel_list_v1, each mapped
|
||||
toplevel window will be sent using the ext_foreign_toplevel_list_v1.toplevel
|
||||
event.
|
||||
|
||||
Clients which only care about the current state can perform a roundtrip after
|
||||
binding this global.
|
||||
|
||||
For each instance of ext_foreign_toplevel_list_v1, the compositor must
|
||||
create a new ext_foreign_toplevel_handle_v1 object for each mapped toplevel.
|
||||
|
||||
If a compositor implementation sends the ext_foreign_toplevel_list_v1.finished
|
||||
event after the global is bound, the compositor must not send any
|
||||
ext_foreign_toplevel_list_v1.toplevel events.
|
||||
</description>
|
||||
|
||||
<event name="toplevel">
|
||||
<description summary="a toplevel has been created">
|
||||
This event is emitted whenever a new toplevel window is created. It is
|
||||
emitted for all toplevels, regardless of the app that has created them.
|
||||
|
||||
All initial properties of the toplevel (identifier, title, app_id) will be sent
|
||||
immediately after this event using the corresponding events for
|
||||
ext_foreign_toplevel_handle_v1. The compositor will use the
|
||||
ext_foreign_toplevel_handle_v1.done event to indicate when all data has
|
||||
been sent.
|
||||
</description>
|
||||
<arg name="toplevel" type="new_id" interface="ext_foreign_toplevel_handle_v1"/>
|
||||
</event>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the compositor has finished with the toplevel manager">
|
||||
This event indicates that the compositor is done sending events
|
||||
to this object. The client should destroy the object.
|
||||
See ext_foreign_toplevel_list_v1.destroy for more information.
|
||||
|
||||
The compositor must not send any more toplevel events after this event.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="stop">
|
||||
<description summary="stop sending events">
|
||||
This request indicates that the client no longer wishes to receive
|
||||
events for new toplevels.
|
||||
|
||||
The Wayland protocol is asynchronous, meaning the compositor may send
|
||||
further toplevel events until the stop request is processed.
|
||||
The client should wait for a ext_foreign_toplevel_list_v1.finished
|
||||
event before destroying this object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the ext_foreign_toplevel_list_v1 object">
|
||||
This request should be called either when the client will no longer
|
||||
use the ext_foreign_toplevel_list_v1 or after the finished event
|
||||
has been received to allow destruction of the object.
|
||||
|
||||
If a client wishes to destroy this object it should send a
|
||||
ext_foreign_toplevel_list_v1.stop request and wait for a ext_foreign_toplevel_list_v1.finished
|
||||
event, then destroy the handles and then this object.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="ext_foreign_toplevel_handle_v1" version="1">
|
||||
<description summary="a mapped toplevel">
|
||||
A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel
|
||||
window. A single app may have multiple mapped toplevels.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the ext_foreign_toplevel_handle_v1 object">
|
||||
This request should be used when the client will no longer use the handle
|
||||
or after the closed event has been received to allow destruction of the
|
||||
object.
|
||||
|
||||
When a handle is destroyed, a new handle may not be created by the server
|
||||
until the toplevel is unmapped and then remapped. Destroying a toplevel handle
|
||||
is not recommended unless the client is cleaning up child objects
|
||||
before destroying the ext_foreign_toplevel_list_v1 object, the toplevel
|
||||
was closed or the toplevel handle will not be used in the future.
|
||||
|
||||
Other protocols which extend the ext_foreign_toplevel_handle_v1
|
||||
interface should require destructors for extension interfaces be
|
||||
called before allowing the toplevel handle to be destroyed.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="the toplevel has been closed">
|
||||
The server will emit no further events on the ext_foreign_toplevel_handle_v1
|
||||
after this event. Any requests received aside from the destroy request must
|
||||
be ignored. Upon receiving this event, the client should destroy the handle.
|
||||
|
||||
Other protocols which extend the ext_foreign_toplevel_handle_v1
|
||||
interface must also ignore requests other than destructors.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="done">
|
||||
<description summary="all information about the toplevel has been sent">
|
||||
This event is sent after all changes in the toplevel state have
|
||||
been sent.
|
||||
|
||||
This allows changes to the ext_foreign_toplevel_handle_v1 properties
|
||||
to be atomically applied. Other protocols which extend the
|
||||
ext_foreign_toplevel_handle_v1 interface may use this event to also
|
||||
atomically apply any pending state.
|
||||
|
||||
This event must not be sent after the ext_foreign_toplevel_handle_v1.closed
|
||||
event.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="title">
|
||||
<description summary="title change">
|
||||
The title of the toplevel has changed.
|
||||
|
||||
The configured state must not be applied immediately. See
|
||||
ext_foreign_toplevel_handle_v1.done for details.
|
||||
</description>
|
||||
<arg name="title" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="app_id">
|
||||
<description summary="app_id change">
|
||||
The app id of the toplevel has changed.
|
||||
|
||||
The configured state must not be applied immediately. See
|
||||
ext_foreign_toplevel_handle_v1.done for details.
|
||||
</description>
|
||||
<arg name="app_id" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="identifier">
|
||||
<description summary="a stable identifier for a toplevel">
|
||||
This identifier is used to check if two or more toplevel handles belong
|
||||
to the same toplevel.
|
||||
|
||||
The identifier is useful for command line tools or privileged clients
|
||||
which may need to reference an exact toplevel across processes or
|
||||
instances of the ext_foreign_toplevel_list_v1 global.
|
||||
|
||||
The compositor must only send this event when the handle is created.
|
||||
|
||||
The identifier must be unique per toplevel and it's handles. Two different
|
||||
toplevels must not have the same identifier. The identifier is only valid
|
||||
as long as the toplevel is mapped. If the toplevel is unmapped the identifier
|
||||
must not be reused. An identifier must not be reused by the compositor to
|
||||
ensure there are no races when sharing identifiers between processes.
|
||||
|
||||
An identifier is a string that contains up to 32 printable ASCII bytes.
|
||||
An identifier must not be an empty string. It is recommended that a
|
||||
compositor includes an opaque generation value in identifiers. How the
|
||||
generation value is used when generating the identifier is implementation
|
||||
dependent.
|
||||
</description>
|
||||
<arg name="identifier" type="string"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="ext_image_capture_source_v1">
|
||||
<copyright>
|
||||
Copyright © 2022 Andri Yngvason
|
||||
Copyright © 2024 Simon Ser
|
||||
|
||||
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.
|
||||
</copyright>
|
||||
|
||||
<description summary="opaque image capture source objects">
|
||||
This protocol serves as an intermediary between capturing protocols and
|
||||
potential image capture sources such as outputs and toplevels.
|
||||
|
||||
This protocol may be extended to support more image capture sources in the
|
||||
future, thereby adding those image capture sources to other protocols that
|
||||
use the image capture source object without having to modify those
|
||||
protocols.
|
||||
|
||||
Warning! The protocol described in this file is currently in the testing
|
||||
phase. Backward compatible changes may be added together with the
|
||||
corresponding interface version bump. Backward incompatible changes can
|
||||
only be done by creating a new major version of the extension.
|
||||
</description>
|
||||
|
||||
<interface name="ext_image_capture_source_v1" version="1">
|
||||
<description summary="opaque image capture source object">
|
||||
The image capture source object is an opaque descriptor for a capturable
|
||||
resource. This resource may be any sort of entity from which an image
|
||||
may be derived.
|
||||
|
||||
Note, because ext_image_capture_source_v1 objects are created from multiple
|
||||
independent factory interfaces, the ext_image_capture_source_v1 interface is
|
||||
frozen at version 1.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="delete this object">
|
||||
Destroys the image capture source. This request may be sent at any time
|
||||
by the client.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="ext_output_image_capture_source_manager_v1" version="1">
|
||||
<description summary="image capture source manager for outputs">
|
||||
A manager for creating image capture source objects for wl_output objects.
|
||||
</description>
|
||||
|
||||
<request name="create_source">
|
||||
<description summary="create source object for output">
|
||||
Creates a source object for an output. Images captured from this source
|
||||
will show the same content as the output. Some elements may be omitted,
|
||||
such as cursors and overlays that have been marked as transparent to
|
||||
capturing.
|
||||
</description>
|
||||
<arg name="source" type="new_id" interface="ext_image_capture_source_v1"/>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="delete this object">
|
||||
Destroys the manager. This request may be sent at any time by the client
|
||||
and objects created by the manager will remain valid after its
|
||||
destruction.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="ext_foreign_toplevel_image_capture_source_manager_v1" version="1">
|
||||
<description summary="image capture source manager for foreign toplevels">
|
||||
A manager for creating image capture source objects for
|
||||
ext_foreign_toplevel_handle_v1 objects.
|
||||
</description>
|
||||
|
||||
<request name="create_source">
|
||||
<description summary="create source object for foreign toplevel">
|
||||
Creates a source object for a foreign toplevel handle. Images captured
|
||||
from this source will show the same content as the toplevel.
|
||||
</description>
|
||||
<arg name="source" type="new_id" interface="ext_image_capture_source_v1"/>
|
||||
<arg name="toplevel_handle" type="object" interface="ext_foreign_toplevel_handle_v1"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="delete this object">
|
||||
Destroys the manager. This request may be sent at any time by the client
|
||||
and objects created by the manager will remain valid after its
|
||||
destruction.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
@ -0,0 +1,456 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="ext_image_copy_capture_v1">
|
||||
<copyright>
|
||||
Copyright © 2021-2023 Andri Yngvason
|
||||
Copyright © 2024 Simon Ser
|
||||
|
||||
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.
|
||||
</copyright>
|
||||
|
||||
<description summary="image capturing into client buffers">
|
||||
This protocol allows clients to ask the compositor to capture image sources
|
||||
such as outputs and toplevels into user submitted buffers.
|
||||
|
||||
Warning! The protocol described in this file is currently in the testing
|
||||
phase. Backward compatible changes may be added together with the
|
||||
corresponding interface version bump. Backward incompatible changes can
|
||||
only be done by creating a new major version of the extension.
|
||||
</description>
|
||||
|
||||
<interface name="ext_image_copy_capture_manager_v1" version="1">
|
||||
<description summary="manager to inform clients and begin capturing">
|
||||
This object is a manager which offers requests to start capturing from a
|
||||
source.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_option" value="1" summary="invalid option flag"/>
|
||||
</enum>
|
||||
|
||||
<enum name="options" bitfield="true">
|
||||
<entry name="paint_cursors" value="1" summary="paint cursors onto captured frames"/>
|
||||
</enum>
|
||||
|
||||
<request name="create_session">
|
||||
<description summary="capture an image capture source">
|
||||
Create a capturing session for an image capture source.
|
||||
|
||||
If the paint_cursors option is set, cursors shall be composited onto
|
||||
the captured frame. The cursor must not be composited onto the frame
|
||||
if this flag is not set.
|
||||
|
||||
If the options bitfield is invalid, the invalid_option protocol error
|
||||
is sent.
|
||||
</description>
|
||||
<arg name="session" type="new_id" interface="ext_image_copy_capture_session_v1"/>
|
||||
<arg name="source" type="object" interface="ext_image_capture_source_v1"/>
|
||||
<arg name="options" type="uint" enum="options"/>
|
||||
</request>
|
||||
|
||||
<request name="create_pointer_cursor_session">
|
||||
<description summary="capture the pointer cursor of an image capture source">
|
||||
Create a cursor capturing session for the pointer of an image capture
|
||||
source.
|
||||
</description>
|
||||
<arg name="session" type="new_id" interface="ext_image_copy_capture_cursor_session_v1"/>
|
||||
<arg name="source" type="object" interface="ext_image_capture_source_v1"/>
|
||||
<arg name="pointer" type="object" interface="wl_pointer"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the manager">
|
||||
Destroy the manager object.
|
||||
|
||||
Other objects created via this interface are unaffected.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="ext_image_copy_capture_session_v1" version="1">
|
||||
<description summary="image copy capture session">
|
||||
This object represents an active image copy capture session.
|
||||
|
||||
After a capture session is created, buffer constraint events will be
|
||||
emitted from the compositor to tell the client which buffer types and
|
||||
formats are supported for reading from the session. The compositor may
|
||||
re-send buffer constraint events whenever they change.
|
||||
|
||||
The advertise buffer constraints, the compositor must send in no
|
||||
particular order: zero or more shm_format and dmabuf_format events, zero
|
||||
or one dmabuf_device event, and exactly one buffer_size event. Then the
|
||||
compositor must send a done event.
|
||||
|
||||
When the client has received all the buffer constraints, it can create a
|
||||
buffer accordingly, attach it to the capture session using the
|
||||
attach_buffer request, set the buffer damage using the damage_buffer
|
||||
request and then send the capture request.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="duplicate_frame" value="1"
|
||||
summary="create_frame sent before destroying previous frame"/>
|
||||
</enum>
|
||||
|
||||
<event name="buffer_size">
|
||||
<description summary="image capture source dimensions">
|
||||
Provides the dimensions of the source image in buffer pixel coordinates.
|
||||
|
||||
The client must attach buffers that match this size.
|
||||
</description>
|
||||
<arg name="width" type="uint" summary="buffer width"/>
|
||||
<arg name="height" type="uint" summary="buffer height"/>
|
||||
</event>
|
||||
|
||||
<event name="shm_format">
|
||||
<description summary="shm buffer format">
|
||||
Provides the format that must be used for shared-memory buffers.
|
||||
|
||||
This event may be emitted multiple times, in which case the client may
|
||||
choose any given format.
|
||||
</description>
|
||||
<arg name="format" type="uint" enum="wl_shm.format" summary="shm format"/>
|
||||
</event>
|
||||
|
||||
<event name="dmabuf_device">
|
||||
<description summary="dma-buf device">
|
||||
This event advertises the device buffers must be allocated on for
|
||||
dma-buf buffers.
|
||||
|
||||
In general the device is a DRM node. The DRM node type (primary vs.
|
||||
render) is unspecified. Clients must not rely on the compositor sending
|
||||
a particular node type. Clients cannot check two devices for equality
|
||||
by comparing the dev_t value.
|
||||
</description>
|
||||
<arg name="device" type="array" summary="device dev_t value"/>
|
||||
</event>
|
||||
|
||||
<event name="dmabuf_format">
|
||||
<description summary="dma-buf format">
|
||||
Provides the format that must be used for dma-buf buffers.
|
||||
|
||||
The client may choose any of the modifiers advertised in the array of
|
||||
64-bit unsigned integers.
|
||||
|
||||
This event may be emitted multiple times, in which case the client may
|
||||
choose any given format.
|
||||
</description>
|
||||
<arg name="format" type="uint" summary="drm format code"/>
|
||||
<arg name="modifiers" type="array" summary="drm format modifiers"/>
|
||||
</event>
|
||||
|
||||
<event name="done">
|
||||
<description summary="all constraints have been sent">
|
||||
This event is sent once when all buffer constraint events have been
|
||||
sent.
|
||||
|
||||
The compositor must always end a batch of buffer constraint events with
|
||||
this event, regardless of whether it sends the initial constraints or
|
||||
an update.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="stopped">
|
||||
<description summary="session is no longer available">
|
||||
This event indicates that the capture session has stopped and is no
|
||||
longer available. This can happen in a number of cases, e.g. when the
|
||||
underlying source is destroyed, if the user decides to end the image
|
||||
capture, or if an unrecoverable runtime error has occurred.
|
||||
|
||||
The client should destroy the session after receiving this event.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="create_frame">
|
||||
<description summary="create a frame">
|
||||
Create a capture frame for this session.
|
||||
|
||||
At most one frame object can exist for a given session at any time. If
|
||||
a client sends a create_frame request before a previous frame object
|
||||
has been destroyed, the duplicate_frame protocol error is raised.
|
||||
</description>
|
||||
<arg name="frame" type="new_id" interface="ext_image_copy_capture_frame_v1"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="delete this object">
|
||||
Destroys the session. This request can be sent at any time by the
|
||||
client.
|
||||
|
||||
This request doesn't affect ext_image_copy_capture_frame_v1 objects created by
|
||||
this object.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="ext_image_copy_capture_frame_v1" version="1">
|
||||
<description summary="image capture frame">
|
||||
This object represents an image capture frame.
|
||||
|
||||
The client should attach a buffer, damage the buffer, and then send a
|
||||
capture request.
|
||||
|
||||
If the capture is successful, the compositor must send the frame metadata
|
||||
(transform, damage, presentation_time in any order) followed by the ready
|
||||
event.
|
||||
|
||||
If the capture fails, the compositor must send the failed event.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="no_buffer" value="1" summary="capture sent without attach_buffer"/>
|
||||
<entry name="invalid_buffer_damage" value="2" summary="invalid buffer damage"/>
|
||||
<entry name="already_captured" value="3" summary="capture request has been sent"/>
|
||||
</enum>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy this object">
|
||||
Destroys the session. This request can be sent at any time by the
|
||||
client.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="attach_buffer">
|
||||
<description summary="attach buffer to session">
|
||||
Attach a buffer to the session.
|
||||
|
||||
The wl_buffer.release request is unused.
|
||||
|
||||
The new buffer replaces any previously attached buffer.
|
||||
|
||||
This request must not be sent after capture, or else the
|
||||
already_captured protocol error is raised.
|
||||
</description>
|
||||
<arg name="buffer" type="object" interface="wl_buffer"/>
|
||||
</request>
|
||||
|
||||
<request name="damage_buffer">
|
||||
<description summary="damage buffer">
|
||||
Apply damage to the buffer which is to be captured next. This request
|
||||
may be sent multiple times to describe a region.
|
||||
|
||||
The client indicates the accumulated damage since this wl_buffer was
|
||||
last captured. During capture, the compositor will update the buffer
|
||||
with at least the union of the region passed by the client and the
|
||||
region advertised by ext_image_copy_capture_frame_v1.damage.
|
||||
|
||||
When a wl_buffer is captured for the first time, or when the client
|
||||
doesn't track damage, the client must damage the whole buffer.
|
||||
|
||||
This is for optimisation purposes. The compositor may use this
|
||||
information to reduce copying.
|
||||
|
||||
These coordinates originate from the upper left corner of the buffer.
|
||||
|
||||
If x or y are strictly negative, or if width or height are negative or
|
||||
zero, the invalid_buffer_damage protocol error is raised.
|
||||
|
||||
This request must not be sent after capture, or else the
|
||||
already_captured protocol error is raised.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="region x coordinate"/>
|
||||
<arg name="y" type="int" summary="region y coordinate"/>
|
||||
<arg name="width" type="int" summary="region width"/>
|
||||
<arg name="height" type="int" summary="region height"/>
|
||||
</request>
|
||||
|
||||
<request name="capture">
|
||||
<description summary="capture a frame">
|
||||
Capture a frame.
|
||||
|
||||
Unless this is the first successful captured frame performed in this
|
||||
session, the compositor may wait an indefinite amount of time for the
|
||||
source content to change before performing the copy.
|
||||
|
||||
This request may only be sent once, or else the already_captured
|
||||
protocol error is raised. A buffer must be attached before this request
|
||||
is sent, or else the no_buffer protocol error is raised.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="transform">
|
||||
<description summary="buffer transform">
|
||||
This event is sent before the ready event and holds the transform that
|
||||
the compositor has applied to the buffer contents.
|
||||
</description>
|
||||
<arg name="transform" type="uint" enum="wl_output.transform"/>
|
||||
</event>
|
||||
|
||||
<event name="damage">
|
||||
<description summary="buffer damaged region">
|
||||
This event is sent before the ready event. It may be generated multiple
|
||||
times to describe a region.
|
||||
|
||||
The first captured frame in a session will always carry full damage.
|
||||
Subsequent frames' damaged regions describe which parts of the buffer
|
||||
have changed since the last ready event.
|
||||
|
||||
These coordinates originate in the upper left corner of the buffer.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="damage x coordinate"/>
|
||||
<arg name="y" type="int" summary="damage y coordinate"/>
|
||||
<arg name="width" type="int" summary="damage width"/>
|
||||
<arg name="height" type="int" summary="damage height"/>
|
||||
</event>
|
||||
|
||||
<event name="presentation_time">
|
||||
<description summary="presentation time of the frame">
|
||||
This event indicates the time at which the frame is presented to the
|
||||
output in system monotonic time. This event is sent before the ready
|
||||
event.
|
||||
|
||||
The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
|
||||
each component being an unsigned 32-bit value. Whole seconds are in
|
||||
tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
|
||||
and the additional fractional part in tv_nsec as nanoseconds. Hence,
|
||||
for valid timestamps tv_nsec must be in [0, 999999999].
|
||||
</description>
|
||||
<arg name="tv_sec_hi" type="uint"
|
||||
summary="high 32 bits of the seconds part of the timestamp"/>
|
||||
<arg name="tv_sec_lo" type="uint"
|
||||
summary="low 32 bits of the seconds part of the timestamp"/>
|
||||
<arg name="tv_nsec" type="uint"
|
||||
summary="nanoseconds part of the timestamp"/>
|
||||
</event>
|
||||
|
||||
<event name="ready">
|
||||
<description summary="frame is available for reading">
|
||||
Called as soon as the frame is copied, indicating it is available
|
||||
for reading.
|
||||
|
||||
The buffer may be re-used by the client after this event.
|
||||
|
||||
After receiving this event, the client must destroy the object.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<enum name="failure_reason">
|
||||
<entry name="unknown" value="0">
|
||||
<description summary="unknown runtime error">
|
||||
An unspecified runtime error has occurred. The client may retry.
|
||||
</description>
|
||||
</entry>
|
||||
<entry name="buffer_constraints" value="1">
|
||||
<description summary="buffer constraints mismatch">
|
||||
The buffer submitted by the client doesn't match the latest session
|
||||
constraints. The client should re-allocate its buffers and retry.
|
||||
</description>
|
||||
</entry>
|
||||
<entry name="stopped" value="2">
|
||||
<description summary="session is no longer available">
|
||||
The session has stopped. See ext_image_copy_capture_session_v1.stopped.
|
||||
</description>
|
||||
</entry>
|
||||
</enum>
|
||||
|
||||
<event name="failed">
|
||||
<description summary="capture failed">
|
||||
This event indicates that the attempted frame copy has failed.
|
||||
|
||||
After receiving this event, the client must destroy the object.
|
||||
</description>
|
||||
<arg name="reason" type="uint" enum="failure_reason"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="ext_image_copy_capture_cursor_session_v1" version="1">
|
||||
<description summary="cursor capture session">
|
||||
This object represents a cursor capture session. It extends the base
|
||||
capture session with cursor-specific metadata.
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="duplicate_session" value="1" summary="get_captuerer_session sent twice"/>
|
||||
</enum>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="delete this object">
|
||||
Destroys the session. This request can be sent at any time by the
|
||||
client.
|
||||
|
||||
This request doesn't affect ext_image_copy_capture_frame_v1 objects created by
|
||||
this object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="get_capture_session">
|
||||
<description summary="get image copy captuerer session">
|
||||
Gets the image copy capture session for this cursor session.
|
||||
|
||||
The session will produce frames of the cursor image. The compositor may
|
||||
pause the session when the cursor leaves the captured area.
|
||||
|
||||
This request must not be sent more than once, or else the
|
||||
duplicate_session protocol error is raised.
|
||||
</description>
|
||||
<arg name="session" type="new_id" interface="ext_image_copy_capture_session_v1"/>
|
||||
</request>
|
||||
|
||||
<event name="enter">
|
||||
<description summary="cursor entered captured area">
|
||||
Sent when a cursor enters the captured area. It shall be generated
|
||||
before the "position" and "hotspot" events when and only when a cursor
|
||||
enters the area.
|
||||
|
||||
The cursor enters the captured area when the cursor image intersects
|
||||
with the captured area. Note, this is different from e.g.
|
||||
wl_pointer.enter.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="leave">
|
||||
<description summary="cursor left captured area">
|
||||
Sent when a cursor leaves the captured area. No "position" or "hotspot"
|
||||
event is generated for the cursor until the cursor enters the captured
|
||||
area again.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="position">
|
||||
<description summary="position changed">
|
||||
Cursors outside the image capture source do not get captured and no
|
||||
event will be generated for them.
|
||||
|
||||
The given position is the position of the cursor's hotspot and it is
|
||||
relative to the main buffer's top left corner in transformed buffer
|
||||
pixel coordinates. The coordinates may be negative or greater than the
|
||||
main buffer size.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="position x coordinates"/>
|
||||
<arg name="y" type="int" summary="position y coordinates"/>
|
||||
</event>
|
||||
|
||||
<event name="hotspot">
|
||||
<description summary="hotspot changed">
|
||||
The hotspot describes the offset between the cursor image and the
|
||||
position of the input device.
|
||||
|
||||
The given coordinates are the hotspot's offset from the origin in
|
||||
buffer coordinates.
|
||||
|
||||
Clients should not apply the hotspot immediately: the hotspot becomes
|
||||
effective when the next ext_image_copy_capture_frame_v1.ready event is received.
|
||||
|
||||
Compositors may delay this event until the client captures a new frame.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="hotspot x coordinates"/>
|
||||
<arg name="y" type="int" summary="hotspot y coordinates"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
@ -0,0 +1,252 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "ext-image-capture-source-v1-protocol.h"
|
||||
#include "ext-image-copy-capture-v1-protocol.h"
|
||||
|
||||
#include "rif.h"
|
||||
|
||||
#define ASSERT(cond, msg)\
|
||||
if(!(cond)) {\
|
||||
fprintf(stderr, "Runtime assertion %s at %s:%d failed: %s\n", #cond, __FILE__, __LINE__, msg);\
|
||||
exit(-1);\
|
||||
}
|
||||
|
||||
// Not included in any standard header
|
||||
// Thus, declaring manually.
|
||||
// See memfd_create(2).
|
||||
int memfd_create(const char* name, unsigned int flags);
|
||||
|
||||
struct image_data {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t shm_format;
|
||||
};
|
||||
|
||||
struct wl_output* stream_output = NULL;
|
||||
struct wl_shm* wl_shm = NULL;
|
||||
struct wl_buffer* frame_buffer = NULL;
|
||||
struct ext_output_image_capture_source_manager_v1* output_source_manager = NULL;
|
||||
struct ext_image_copy_capture_manager_v1* capture_manager = NULL;
|
||||
|
||||
void* shm_data = NULL;
|
||||
|
||||
struct image_data img_data;
|
||||
|
||||
uint8_t is_frame_ready = 0;
|
||||
uint8_t is_capture_ready = 0;
|
||||
|
||||
uint32_t buf_size;
|
||||
|
||||
void handle_global(
|
||||
void* data,
|
||||
struct wl_registry* registry,
|
||||
uint32_t name,
|
||||
const char* interface,
|
||||
uint32_t version) {
|
||||
if(strcmp(interface, wl_output_interface.name) == 0) {
|
||||
stream_output = wl_registry_bind(registry, name, &wl_output_interface, version);
|
||||
}
|
||||
if(strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, version);
|
||||
}
|
||||
if(strcmp(interface, ext_output_image_capture_source_manager_v1_interface.name) == 0) {
|
||||
output_source_manager = wl_registry_bind(registry, name, &ext_output_image_capture_source_manager_v1_interface, version);
|
||||
}
|
||||
if(strcmp(interface, ext_image_copy_capture_manager_v1_interface.name) == 0) {
|
||||
capture_manager = wl_registry_bind(registry, name, &ext_image_copy_capture_manager_v1_interface, version);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_global_remove(
|
||||
void* data,
|
||||
struct wl_registry* registry,
|
||||
uint32_t name) {
|
||||
// Who cares
|
||||
}
|
||||
|
||||
const struct wl_registry_listener reg_callbacks = {
|
||||
.global = handle_global,
|
||||
.global_remove = handle_global_remove,
|
||||
};
|
||||
|
||||
void buffer_size(void *data,
|
||||
struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1,
|
||||
uint32_t width,
|
||||
uint32_t height) {
|
||||
printf("x: %d, y: %d\n", width, height);
|
||||
img_data.width = width;
|
||||
img_data.height = height;
|
||||
}
|
||||
|
||||
void shm_format(void *data,
|
||||
struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1,
|
||||
uint32_t format) {
|
||||
img_data.shm_format = format;
|
||||
ASSERT(format == WL_SHM_FORMAT_XBGR8888 || format == WL_SHM_FORMAT_ABGR8888, "Unsupported buffer format.");
|
||||
}
|
||||
|
||||
void dmabuf_device(void *data,
|
||||
struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1,
|
||||
struct wl_array *device) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
void dmabuf_format(void *data,
|
||||
struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1,
|
||||
uint32_t format,
|
||||
struct wl_array *modifiers) {
|
||||
|
||||
}
|
||||
|
||||
void done(void *data,
|
||||
struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1) {
|
||||
const int shm_fd = memfd_create("wl_shm data", 0);
|
||||
ASSERT(shm_fd != -1, "Failed to create memfd.");
|
||||
|
||||
// Format is hardcoded to chan width 4
|
||||
const uint32_t chan_width = 4;
|
||||
const uint32_t stride = img_data.width * chan_width;
|
||||
buf_size = img_data.height * stride;
|
||||
|
||||
ASSERT(ftruncate(shm_fd, buf_size) != -1, "Failed to ftruncate the memfd.");
|
||||
|
||||
shm_data = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
||||
ASSERT(shm_data != MAP_FAILED, "Failed to mmap the memfd.");
|
||||
|
||||
struct wl_shm_pool* pool = wl_shm_create_pool(wl_shm, shm_fd, buf_size);
|
||||
ASSERT(pool != NULL, "Failed to create the wl_shm_pool.");
|
||||
|
||||
frame_buffer = wl_shm_pool_create_buffer(pool, 0, img_data.width, img_data.height, stride, img_data.shm_format);
|
||||
ASSERT(frame_buffer != NULL, "Failed to create the wl_buffer.");
|
||||
|
||||
is_frame_ready = 1;
|
||||
}
|
||||
|
||||
|
||||
void stopped(void *data,
|
||||
struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1) {
|
||||
|
||||
}
|
||||
|
||||
const struct ext_image_copy_capture_session_v1_listener capture_listener = {
|
||||
.buffer_size = buffer_size,
|
||||
.shm_format = shm_format,
|
||||
.dmabuf_device = dmabuf_device,
|
||||
.dmabuf_format = dmabuf_format,
|
||||
.done = done,
|
||||
.stopped = stopped
|
||||
};
|
||||
|
||||
|
||||
void transform(void *data,
|
||||
struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1,
|
||||
uint32_t transform) {
|
||||
|
||||
}
|
||||
void damage(void *data,
|
||||
struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t width,
|
||||
int32_t height) {
|
||||
|
||||
}
|
||||
void presentation_time(void *data,
|
||||
struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1,
|
||||
uint32_t tv_sec_hi,
|
||||
uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec) {
|
||||
|
||||
}
|
||||
void ready(void *data,
|
||||
struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1) {
|
||||
is_capture_ready = 1;
|
||||
}
|
||||
void failed(void *data,
|
||||
struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1,
|
||||
uint32_t reason) {
|
||||
switch(reason) {
|
||||
case 0:
|
||||
printf("Capture failed. Reason: Unknown Runtime Error.");
|
||||
break;
|
||||
case 1:
|
||||
printf("Capture failed. Reason: Buffer Constraints Mismatch.");
|
||||
break;
|
||||
case 2:
|
||||
printf("Capture failed. Reason: Session Is No Longer Available.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const struct ext_image_copy_capture_frame_v1_listener frame_listener = {
|
||||
.transform = transform,
|
||||
.damage = damage,
|
||||
.presentation_time = presentation_time,
|
||||
.ready = ready,
|
||||
.failed = failed
|
||||
};
|
||||
|
||||
|
||||
int main() {
|
||||
struct wl_display* dpy = wl_display_connect(NULL);
|
||||
ASSERT(dpy != NULL, "Unable to connect to Wayland");
|
||||
|
||||
struct wl_registry* registry = wl_display_get_registry(dpy);
|
||||
wl_registry_add_listener(registry, ®_callbacks, NULL);
|
||||
wl_display_roundtrip(dpy);
|
||||
|
||||
ASSERT(wl_shm != NULL, "core.wl_shm not supported.");
|
||||
|
||||
ASSERT(stream_output != NULL, "No outputs found!");
|
||||
|
||||
ASSERT(output_source_manager != NULL, "ext-image-capture-source-v1.ext_output_image_capture_source_manager_v1 not supported.");
|
||||
ASSERT(capture_manager != NULL, "ext-image-copy-capture-v1.ext_image_copy_capture_manager_v1 not supported.");
|
||||
|
||||
struct ext_image_capture_source_v1* capture_source =
|
||||
ext_output_image_capture_source_manager_v1_create_source(output_source_manager, stream_output);
|
||||
ASSERT(capture_source != NULL, "Failed creating a source for the default output!")
|
||||
|
||||
struct ext_image_copy_capture_session_v1* capture_session =
|
||||
ext_image_copy_capture_manager_v1_create_session(capture_manager, capture_source, EXT_IMAGE_COPY_CAPTURE_MANAGER_V1_OPTIONS_PAINT_CURSORS);
|
||||
ASSERT(capture_session != NULL, "Failed creating a session for the capture!");
|
||||
|
||||
ext_image_copy_capture_session_v1_add_listener(capture_session, &capture_listener, NULL);
|
||||
wl_display_roundtrip(dpy);
|
||||
|
||||
while(is_frame_ready == 0 && wl_display_dispatch(dpy) != -1) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
struct ext_image_copy_capture_frame_v1* frame = ext_image_copy_capture_session_v1_create_frame(capture_session);
|
||||
ASSERT(frame != NULL, "Failed to create frame!");
|
||||
|
||||
ext_image_copy_capture_frame_v1_add_listener(frame, &frame_listener, NULL);
|
||||
|
||||
ext_image_copy_capture_frame_v1_attach_buffer(frame, frame_buffer);
|
||||
ext_image_copy_capture_frame_v1_damage_buffer(frame, 0, 0, img_data.width, img_data.height);
|
||||
ext_image_copy_capture_frame_v1_capture(frame);
|
||||
|
||||
wl_display_roundtrip(dpy);
|
||||
|
||||
while(is_capture_ready == 0 && wl_display_dispatch(dpy) != -1) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
printf("Saving screenshot.\n");
|
||||
|
||||
// void write_rif_little(char* path, uint32_t width, uint32_t size, uint8_t format, void* data) {
|
||||
// Thanks to endianess shenanigans, ABGR/XBGR is RGBA.
|
||||
write_rif_little("out.rif", img_data.width, buf_size, RIF_FORMAT_R8G8B8A8, shm_data);
|
||||
|
||||
ext_image_copy_capture_frame_v1_destroy(frame);
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
typedef struct rif {
|
||||
// "lifV0001"
|
||||
// "rifV0001"
|
||||
char magic_num[8]; // Big Endian. UTF-8 Encoded. 6C 69 66 56 30 30 30 31
|
||||
uint64_t width; // Big Endian
|
||||
uint8_t format;
|
||||
uint8_t data[];
|
||||
} rif_t;
|
||||
|
||||
#define RIF_MAGIC_LITTLE {'l', 'i', 'f', 'V', '0', '0', '0', '1'}
|
||||
#define RIF_MAGIC_BIG {'r', 'i', 'f', 'V', '0', '0', '0', '1'}
|
||||
|
||||
#define RIF_FORMAT_R8G8B8 0
|
||||
#define RIF_FORMAT_R8G8B8A8 1
|
||||
|
||||
void write_rif_little(char* path, uint32_t width, uint32_t size, uint8_t format, void* data) {
|
||||
#define IMG_SIZE 17 + size // 17 is the size of raw_img excluding the data field
|
||||
rif_t* img = malloc(IMG_SIZE);
|
||||
char magic[] = RIF_MAGIC_LITTLE;
|
||||
memcpy(((char*)img)+0, magic, 8);
|
||||
img->width = width;
|
||||
img->format = format;
|
||||
memcpy(((char*)img)+17, data, size);
|
||||
FILE* output_file = fopen(path, "w");
|
||||
fwrite(img, 1, IMG_SIZE, output_file);
|
||||
}
|
Loading…
Reference in new issue