Mercurial > minori
changeset 199:9f3534f6b8c4
dep/animia: initial Wayland support, drop non-working kvm fd plugin
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Tue, 02 Jan 2024 02:34:27 -0500 |
parents | bc1ae1810855 |
children | da91af31ae73 |
files | dep/animia/CMakeLists.txt dep/animia/README.md dep/animia/data/ext-foreign-toplevel-list-v1.xml dep/animia/include/animia/win/wayland.h dep/animia/include/animia/win/wayland/ext-foreign-toplevel-list-v1.h dep/animia/src/fd/kvm.cc dep/animia/src/win.cc dep/animia/src/win/wayland.cc dep/animia/src/win/wayland/ext-foreign-toplevel-list-v1.c dep/animia/src/win/x11.cc |
diffstat | 10 files changed, 1049 insertions(+), 193 deletions(-) [+] |
line wrap: on
line diff
--- a/dep/animia/CMakeLists.txt Sun Dec 24 02:59:42 2023 -0500 +++ b/dep/animia/CMakeLists.txt Tue Jan 02 02:34:27 2024 -0500 @@ -60,9 +60,7 @@ if(LINUX) list(APPEND DEFINES LINUX) - list(APPEND SRC_FILES - src/fd/proc.cc - ) + list(APPEND SRC_FILES src/fd/proc.cc) elseif(LIBUTIL_LIBRARY) # FreeBSD's libutil get_filename_component(LIBUTIL_DIR ${LIBUTIL_LIBRARY} DIRECTORY) @@ -88,18 +86,20 @@ endif() # LINUX # X11 - find_package(X11 COMPONENTS X11) + find_package(X11 COMPONENTS X11 XRes) + if(X11_FOUND) + # Getting PIDs from windows... + if (X11_XRes_FOUND) + list(APPEND DEFINES HAVE_XRES) + else() # NOT X11_XRes_FOUND + message(WARNING "libXRes could not be found! Finding PIDs in X11 windows may not work correctly!") + endif() # X11_XRes_FOUND + list(APPEND DEFINES X11) - list(APPEND SRC_FILES - src/win/x11.cc - ) - list(APPEND INCLUDE_DIRS - ${X11_INCLUDE_DIRS} - ) - list(APPEND LIBRARIES - ${X11_LIBRARIES} - ) + list(APPEND SRC_FILES src/win/x11.cc) + list(APPEND INCLUDE_DIRS ${X11_INCLUDE_DIRS}) + list(APPEND LIBRARIES ${X11_LIBRARIES}) # This will include Xres, I think.. else() # NOT X11_FOUND # For some systems, i.e. Debian, FindX11 fails to find X11, so we have # to use pkg_config as a fallback @@ -107,19 +107,40 @@ if(PKG_CONFIG_FOUND) pkg_check_modules(X11 x11) if(X11_FOUND) + # Check for XRes the hard way + find_path(X11_XRes_HEADER "extensions/XRes.h" PATHS ${X11_INCLUDE_DIRS}) + find_library(X11_XRes_LIB XRes ${X11_LIBRARY_DIRS}) + + if(X11_XRes_HEADER AND X11_XRes_LIB) + # TODO: We should REALLY check for XResQueryClientIds here... + list(APPEND DEFINES HAVE_XRES) + list(APPEND LIBRARIES ${X11_XRes_LIB}) + else() + message(WARNING "libXRes could not be found! Finding PIDs in X11 windows may not work correctly!") + endif() + list(APPEND DEFINES X11) - list(APPEND SRC_FILES - src/win/x11.cc - ) - list(APPEND INCLUDE_DIRS - ${X11_INCLUDE_DIRS} - ) - list(APPEND LIBRARIES - ${X11_LINK_LIBRARIES} - ) + list(APPEND SRC_FILES src/win/x11.cc) + list(APPEND INCLUDE_DIRS ${X11_INCLUDE_DIRS}) + list(APPEND LIBRARIES ${X11_LINK_LIBRARIES}) endif() # X11_FOUND endif() # PKG_CONFIG_FOUND endif() # X11_FOUND + + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(WAYLAND wayland-client) + if(WAYLAND_FOUND) + enable_language(C) + list(APPEND DEFINES WAYLAND) + list(APPEND SRC_FILES + src/win/wayland.cc + src/win/wayland/ext-foreign-toplevel-list-v1.c + ) + list(APPEND INCLUDE_DIRS ${WAYLAND_INCLUDE_DIRS}) + list(APPEND LIBRARIES ${WAYLAND_LINK_LIBRARIES}) + endif() # WAYLAND_FOUND + endif() # PKG_CONFIG_FOUND endif() # WIN32 AND APPLE add_library(animia SHARED ${SRC_FILES}) @@ -128,6 +149,6 @@ CXX_STANDARD 17 ) -target_compile_definitions(animia PUBLIC ${DEFINES}) +target_compile_definitions(animia PRIVATE ${DEFINES}) target_include_directories(animia PRIVATE include PUBLIC ${INCLUDE_DIRS}) target_link_libraries(animia PUBLIC ${LIBRARIES})
--- a/dep/animia/README.md Sun Dec 24 02:59:42 2023 -0500 +++ b/dep/animia/README.md Tue Jan 02 02:34:27 2024 -0500 @@ -1,27 +1,43 @@ # Animia -Animia is a work-in-progress cross-platform hard fork of Anisthesia and part of Minori. +Animia is a work-in-progress cross-platform hard fork of Anisthesia and part of +Minori. -Most (if not all) Anisthesia configs should also work in this library as well (at least on Windows). +Most (if not all) Anisthesia configs should also work in this library as well +(at least on Windows). ## Support -Animia supports Windows, macOS, and Linux when dealing with file descriptors. When enumerating -windows, it supports Windows, macOS (Quartz), and X11. I'd love to be able to support Wayland, but -there's nothing I can do to provide an API that literally does not exist. +Animia supports Windows, macOS, and Linux when dealing with file descriptors. +When enumerating windows, it supports Windows, macOS (Quartz), X11, and +Wayland (only via the `ext_foreign_toplevel_handle_v1` interface). -Unlike Anisthesia, Animia currently does not support UI automation, i.e., some web browsers will not -work properly, if at all. +Unlike Anisthesia, Animia currently does not support UI automation, i.e., most +web browsers will not work properly, if at all. ## Platform-specific quirks +### Windows +To get the currently opened file handles on Windows, Animia has to use internal +kernel functions. However, these functions aren't likely to change anytime soon. + ### macOS -The code to executable names on macOS uses internal functions. However, if these functions -cannot be found for whatever reason, it falls back to parsing the arguments, and then to calling -the kernel. +The code to retrieve executable names on macOS uses internal functions. However, +if these functions cannot be found for whatever reason, it falls back to parsing +the arguments, and then to calling the kernel. -macOS doesn't have the concept of "class names", rather, it has bundle identifiers, which are -a suitable replacement for most use cases. +Additionally, macOS does not have the concept of class names, rather, it has +bundle identifiers, which are a suitable replacement in most use cases, and are +what Animia will try to grab before falling back to the Quartz window name. ### X11 -X11 has no idea what PID started your window. As a result, we can't provide it. Eventually, -there'll be support for the XRes extension which provides this possibility. For now, PIDs are -received using the untrustworthy `_NET_WM_PID` resource. +If your X server has the XRes extension installed, Animia will use it to get +PIDs. Otherwise, X11 has no idea what PID started your window. As a result, +what Animia will give you is from the `_NET_WM_PID` resource, which is +[very](https://stackoverflow.com/a/49970490) +[unreliable](https://stackoverflow.com/a/49970271). + +### Wayland +Only Wayland servers that implement the `ext_foreign_toplevel_handle_v1` +interface will work with Animia. As of 2 January 2024, this means there are no +server implementations that will work. **However**, it is possible to implement +support for the wlroots-specific `wlr_foreign_toplevel_management_unstable_v1` +protocol, which will at least give support to window managers based off of it.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/data/ext-foreign-toplevel-list-v1.xml Tue Jan 02 02:34:27 2024 -0500 @@ -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 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>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/win/wayland.h Tue Jan 02 02:34:27 2024 -0500 @@ -0,0 +1,12 @@ +#ifndef __animia__animia__win__wayland_h +#define __animia__animia__win__wayland_h + +#include "animia/win.h" + +namespace animia::internal::wayland { + +bool EnumerateWindows(window_proc_t window_proc); + +} // namespace animia::internal::wayland + +#endif // __animia__animia__win__wayland_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/include/animia/win/wayland/ext-foreign-toplevel-list-v1.h Tue Jan 02 02:34:27 2024 -0500 @@ -0,0 +1,443 @@ +/* Generated by wayland-scanner 1.21.0 */ + +#ifndef EXT_FOREIGN_TOPLEVEL_LIST_V1_CLIENT_PROTOCOL_H +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_ext_foreign_toplevel_list_v1 The ext_foreign_toplevel_list_v1 protocol + * list toplevels + * + * @section page_desc_ext_foreign_toplevel_list_v1 Description + * + * 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. + * + * @section page_ifaces_ext_foreign_toplevel_list_v1 Interfaces + * - @subpage page_iface_ext_foreign_toplevel_list_v1 - list toplevels + * - @subpage page_iface_ext_foreign_toplevel_handle_v1 - a mapped toplevel + * @section page_copyright_ext_foreign_toplevel_list_v1 Copyright + * <pre> + * + * 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. + * </pre> + */ +struct ext_foreign_toplevel_handle_v1; +struct ext_foreign_toplevel_list_v1; + +#ifndef EXT_FOREIGN_TOPLEVEL_LIST_V1_INTERFACE +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_INTERFACE +/** + * @page page_iface_ext_foreign_toplevel_list_v1 ext_foreign_toplevel_list_v1 + * @section page_iface_ext_foreign_toplevel_list_v1_desc Description + * + * 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. + * @section page_iface_ext_foreign_toplevel_list_v1_api API + * See @ref iface_ext_foreign_toplevel_list_v1. + */ +/** + * @defgroup iface_ext_foreign_toplevel_list_v1 The ext_foreign_toplevel_list_v1 interface + * + * 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. + */ +extern const struct wl_interface ext_foreign_toplevel_list_v1_interface; +#endif +#ifndef EXT_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE +/** + * @page page_iface_ext_foreign_toplevel_handle_v1 ext_foreign_toplevel_handle_v1 + * @section page_iface_ext_foreign_toplevel_handle_v1_desc Description + * + * A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel + * window. A single app may have multiple mapped toplevels. + * @section page_iface_ext_foreign_toplevel_handle_v1_api API + * See @ref iface_ext_foreign_toplevel_handle_v1. + */ +/** + * @defgroup iface_ext_foreign_toplevel_handle_v1 The ext_foreign_toplevel_handle_v1 interface + * + * A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel + * window. A single app may have multiple mapped toplevels. + */ +extern const struct wl_interface ext_foreign_toplevel_handle_v1_interface; +#endif + +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + * @struct ext_foreign_toplevel_list_v1_listener + */ +struct ext_foreign_toplevel_list_v1_listener { + /** + * 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. + */ + void (*toplevel)(void *data, + struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1, + struct ext_foreign_toplevel_handle_v1 *toplevel); + /** + * the compositor has finished with the toplevel manager + * + * This event indicates that the compositor is done sending + * events to this object. The client should 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. + */ + void (*finished)(void *data, + struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1); +}; + +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + */ +static inline int +ext_foreign_toplevel_list_v1_add_listener(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1, + const struct ext_foreign_toplevel_list_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) ext_foreign_toplevel_list_v1, + (void (**)(void)) listener, data); +} + +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_STOP 0 +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_DESTROY 1 + +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_STOP_SINCE_VERSION 1 +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_LIST_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_ext_foreign_toplevel_list_v1 */ +static inline void +ext_foreign_toplevel_list_v1_set_user_data(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) ext_foreign_toplevel_list_v1, user_data); +} + +/** @ingroup iface_ext_foreign_toplevel_list_v1 */ +static inline void * +ext_foreign_toplevel_list_v1_get_user_data(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) ext_foreign_toplevel_list_v1); +} + +static inline uint32_t +ext_foreign_toplevel_list_v1_get_version(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_list_v1); +} + +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + * + * 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. + */ +static inline void +ext_foreign_toplevel_list_v1_stop(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_foreign_toplevel_list_v1, + EXT_FOREIGN_TOPLEVEL_LIST_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_list_v1), 0); +} + +/** + * @ingroup iface_ext_foreign_toplevel_list_v1 + * + * 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. + */ +static inline void +ext_foreign_toplevel_list_v1_destroy(struct ext_foreign_toplevel_list_v1 *ext_foreign_toplevel_list_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_foreign_toplevel_list_v1, + EXT_FOREIGN_TOPLEVEL_LIST_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_list_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + * @struct ext_foreign_toplevel_handle_v1_listener + */ +struct ext_foreign_toplevel_handle_v1_listener { + /** + * 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. + */ + void (*closed)(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1); + /** + * 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. + */ + void (*done)(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1); + /** + * 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. + */ + void (*title)(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, + const char *title); + /** + * 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. + */ + void (*app_id)(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, + const char *app_id); + /** + * 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. + */ + void (*identifier)(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, + const char *identifier); +}; + +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + */ +static inline int +ext_foreign_toplevel_handle_v1_add_listener(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, + const struct ext_foreign_toplevel_handle_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) ext_foreign_toplevel_handle_v1, + (void (**)(void)) listener, data); +} + +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 0 + +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1 +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_IDENTIFIER_SINCE_VERSION 1 + +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + */ +#define EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_ext_foreign_toplevel_handle_v1 */ +static inline void +ext_foreign_toplevel_handle_v1_set_user_data(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) ext_foreign_toplevel_handle_v1, user_data); +} + +/** @ingroup iface_ext_foreign_toplevel_handle_v1 */ +static inline void * +ext_foreign_toplevel_handle_v1_get_user_data(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) ext_foreign_toplevel_handle_v1); +} + +static inline uint32_t +ext_foreign_toplevel_handle_v1_get_version(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_handle_v1); +} + +/** + * @ingroup iface_ext_foreign_toplevel_handle_v1 + * + * 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. + */ +static inline void +ext_foreign_toplevel_handle_v1_destroy(struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) ext_foreign_toplevel_handle_v1, + EXT_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) ext_foreign_toplevel_handle_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif
--- a/dep/animia/src/fd/kvm.cc Sun Dec 24 02:59:42 2023 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -/* kvm.cc: provides support for libkvm in multiple BSDs -** -** this is really the only way to get a thing that works on -** OpenBSD AND NetBSD. -** -** Much of this file is taken from the fstat source code in -** NetBSD. -*/ - -#include <kvm.h> - -namespace animia::internal::kvm { - -static bool GetFilename(kvm_t* kvm, struct vnode *vp, std::string& name) { - struct vnode vn; - if (!kvm_read(kvm, vp, &vn, sizeof(*vn))) - return 0; - - struct filestat fst; - const char* type = vfilestat(vn, &fst); - if (type == dead) - return false; - - for (DEVS* d = devs; d != NULL; d = d->next) { - if (d->fsid == fst->fsid && d->ino == fst->fileid) { - name = d->name; - break; - } - } - - return true; -} - -static bool GetFilePath(kvm_t* kvm, fdfile_t* fp, std::string& path) { - struct file file; - fdfile_t fdfile; - - if (!kvm_read(kvm, fp, &fdfile, sizeof(fdfile))) - return false; - - if (!fdfile.ff_file) - return false; - - if (!kvm_read(fdfile.ff_file, &file, sizeof(file))) - return false; - - if (file.f_type != DTYPE_VNODE) - return false; - - return GetFilename(kvm, file.f_data, path); -} - -static bool OpenFiles(kvm_t* kvm, struct kinfo_proc* p, open_file_proc_t open_file_proc) { - if (p->proc->p_fd == 0 || p->proc->p_cwdi == 0) - return false; - - struct filedesc filed; - if (!kvm_read(kvm, p->proc->p_fd, &filed, sizeof(filed))) - return false; - - if (filed.fd_lastfile == -1) - return false; - - struct cwdinfo cwdi; - if (!kvm_read(kvm, p->proc->p_cwdi, &cwdi, sizeof(cwdi))) - return false; - - struct fdtab dt; - if (!kvm_read(kvm, filed.fd_dt, &dt, sizeof(dt))) - return false; - - /* check for corrupted files? */ - if ((unsigned)filed.fd_lastfile >= dt.dt_nfiles || filed.fd_freefile > filed.fd_lastfile + 1) - return false; - - /* open files */ - std::unique_ptr<fdfile_t*[]> ofiles = nullptr; - { - ofiles.reset(malloc((filed.fd_lastfile + 1) * sizeof(fdfile_t*))); - if (!ofiles.get()) - return false; - } - - if (!kvm_read(kvm, &filed.fd_dt->dt_ff, ofiles.get(), filed.fd_lastfile + 1 * (sizeof(fdfile_t*)))) - return false; - - for (int i = 0; i <= filed.fd_lastfile; i++) { - if (!ofiles[i]) - continue; - std::string name; - GetFilePath(kvm, ofiles[i], name); - if (!open_file_proc(p->proc->p_pid, name)) - return false; - } - - return true; -} - -bool EnumerateOpenProcesses(process_proc_t process_proc) { - char errbuf[_POSIX2_LINE_MAX]; - kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - if (!kernel) - return false; - - int entries = 0; - struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &nentries); - if (!kinfo) - return false; - - for (int i = 0; i < entries; i++) - if (!process_proc({kinfo[i].p_pid, kinfo[i].p_comm})) - return false; - - return true; -} - -bool EnumerateOpenFiles(std::set<pid_t>& pids, open_file_proc_t open_file_proc) { - kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - if (!kernel) - return false; - - for (const auto& pid : pids) { - int cnt; - struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_PID, pid, &cnt); - if (!kinfo) - return false; - - for (int i = 0; i < cnt; i++) { - OpenFiles(kernel, kinfo, open_file_proc); - } - } -} - -}
--- a/dep/animia/src/win.cc Sun Dec 24 02:59:42 2023 -0500 +++ b/dep/animia/src/win.cc Tue Jan 02 02:34:27 2024 -0500 @@ -12,21 +12,29 @@ # include "animia/win/x11.h" #endif +#ifdef WAYLAND +# include "animia/win/wayland.h" +#endif + namespace animia::internal { bool EnumerateWindows(window_proc_t window_proc) { bool success = false; #ifdef WIN32 - success ^= win32::EnumerateWindows(window_proc); + success |= win32::EnumerateWindows(window_proc); #endif #ifdef MACOSX - success ^= quartz::EnumerateWindows(window_proc); + success |= quartz::EnumerateWindows(window_proc); #endif #ifdef X11 - success ^= x11::EnumerateWindows(window_proc); + success |= x11::EnumerateWindows(window_proc); +#endif + +#ifdef WAYLAND + success |= wayland::EnumerateWindows(window_proc); #endif return success;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/win/wayland.cc Tue Jan 02 02:34:27 2024 -0500 @@ -0,0 +1,155 @@ +#include "animia/win/wayland.h" +#include "animia/win.h" +#include "animia.h" + +#include <iostream> +#include <cstring> + +#include "animia/win/wayland/ext-foreign-toplevel-list-v1.h" +#include <wayland-client.h> + +namespace animia::internal::wayland { + +static void noop() {} + +/* ext_foreign_handle stuff */ +static void ext_foreign_handle_handle_app_id(void* data, struct ext_foreign_toplevel_handle_v1* handle, const char* app_id) { + if (app_id) + reinterpret_cast<Window*>(data)->class_name = app_id; +} + +static void ext_foreign_handle_handle_title(void* data, struct ext_foreign_toplevel_handle_v1* handle, const char* title) { + if (title) + reinterpret_cast<Window*>(data)->text = title; +} + +static void ext_foreign_handle_handle_done(void* data, struct ext_foreign_toplevel_handle_v1* handle) { + if (handle) + ext_foreign_toplevel_handle_v1_destroy(handle); +} + +static void ext_foreign_handle_handle_closed(void*, struct ext_foreign_toplevel_handle_v1*) {} +static void ext_foreign_handle_handle_identifier(void*, ext_foreign_toplevel_handle_v1*, const char*) {} + +static const struct ext_foreign_toplevel_handle_v1_listener ext_handle_listener = { + .closed = ext_foreign_handle_handle_closed, + .done = ext_foreign_handle_handle_done, + .title = ext_foreign_handle_handle_title, + .app_id = ext_foreign_handle_handle_app_id, + .identifier = ext_foreign_handle_handle_identifier // for now +}; + +static void ext_toplevel_list_handle_toplevel(void* data, struct ext_foreign_toplevel_list_v1* list, struct ext_foreign_toplevel_handle_v1* handle) { + std::vector<Window>* windows = reinterpret_cast<std::vector<Window>*>(data); + if (!windows) + return; + + windows->push_back({0}); + ext_foreign_toplevel_handle_v1_add_listener(handle, &ext_handle_listener, &*windows->end()); +} + +static void ext_toplevel_list_handle_finished(void*, ext_foreign_toplevel_list_v1*) {} + +static const struct ext_foreign_toplevel_list_v1_listener ext_toplevel_list_listener = { + .toplevel = ext_toplevel_list_handle_toplevel, + .finished = ext_toplevel_list_handle_finished +}; + +/* -- Global data -- */ +struct global_data { + struct ext_foreign_toplevel_list_v1* ext_toplevel_list; + std::vector<Window> windows; +}; + +static void registry_handle_global(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { + struct global_data* global = reinterpret_cast<struct global_data*>(data); + + if (!std::strcmp(interface, ext_foreign_toplevel_list_v1_interface.name)) { + // bind to ext-foreign-toplevel-list-v1 + global->ext_toplevel_list = reinterpret_cast<struct ext_foreign_toplevel_list_v1*>(wl_registry_bind(registry, name, &ext_foreign_toplevel_list_v1_interface, 1)); + ext_foreign_toplevel_list_v1_add_listener(global->ext_toplevel_list, &ext_toplevel_list_listener, &global->windows); + } +} + +static void registry_handle_global_remove(void*, wl_registry*, uint32_t) {} + +static const struct wl_registry_listener registry_listener = { + .global = registry_handle_global, + .global_remove = registry_handle_global_remove +}; + +/* BAH! humbug... */ +struct sync_data { + bool loop = true; + int sync = 0; + + struct global_data global = {0}; + + struct wl_callback* callback = nullptr; + struct wl_display* display = nullptr; +}; + +static void sync_handle_done (void* data, struct wl_callback* callback, uint32_t other_data); +static const struct wl_callback_listener sync_callback_listener = { + .done = sync_handle_done, +}; + +static void sync_handle_done (void* data, struct wl_callback* callback, uint32_t other_data) { + struct sync_data* sync = reinterpret_cast<struct sync_data*>(data); + + wl_callback_destroy(callback); + sync->callback = nullptr; + + if (sync->sync == 0) { + if (!sync->global.ext_toplevel_list) { + std::cerr << "animia/wayland: Wayland server doesn't support ext-foreign-toplevel-list-v1!" << std::endl; + sync->loop = false; + return; + } + + sync->sync++; + sync->callback = wl_display_sync(sync->display); + wl_callback_add_listener(sync->callback, &sync_callback_listener, sync); + + /* we may need another sync here if there are protocol extensions for + * ext_foreign_toplevel_list. + * + * more info: https://git.sr.ht/~leon_plickat/lswt/tree/toplevel-info/item/lswt.c + */ + } else { + /* we've received everything we need! */ + sync->loop = false; + } +} + +/* here comes the actual function we can use */ +bool EnumerateWindows(window_proc_t window_proc) { + struct sync_data sync = { + .display = wl_display_connect(NULL) + }; + + if (!sync.display) { + std::cerr << "animia/wayland: Can't connect to display" << std::endl; + return false; + } + + struct global_data global; + + struct wl_registry* registry = wl_display_get_registry(sync.display); + wl_registry_add_listener(registry, ®istry_listener, &sync.global); + + sync.callback = wl_display_sync(sync.display); + wl_callback_add_listener(sync.callback, &sync_callback_listener, &sync); + + while (sync.loop && wl_display_dispatch(sync.display)); + + for (const auto& window : global.windows) + if (!window_proc({0}, window)) + return false; + + wl_display_disconnect(sync.display); + + return true; +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/win/wayland/ext-foreign-toplevel-list-v1.c Tue Jan 02 02:34:27 2024 -0500 @@ -0,0 +1,85 @@ +/* Generated by wayland-scanner 1.21.0 */ + +/* + * 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. + */ + +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface ext_foreign_toplevel_handle_v1_interface; + +static const struct wl_interface *ext_foreign_toplevel_list_v1_types[] = { + NULL, + &ext_foreign_toplevel_handle_v1_interface, +}; + +static const struct wl_message ext_foreign_toplevel_list_v1_requests[] = { + { "stop", "", ext_foreign_toplevel_list_v1_types + 0 }, + { "destroy", "", ext_foreign_toplevel_list_v1_types + 0 }, +}; + +static const struct wl_message ext_foreign_toplevel_list_v1_events[] = { + { "toplevel", "n", ext_foreign_toplevel_list_v1_types + 1 }, + { "finished", "", ext_foreign_toplevel_list_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface ext_foreign_toplevel_list_v1_interface = { + "ext_foreign_toplevel_list_v1", 1, + 2, ext_foreign_toplevel_list_v1_requests, + 2, ext_foreign_toplevel_list_v1_events, +}; + +static const struct wl_message ext_foreign_toplevel_handle_v1_requests[] = { + { "destroy", "", ext_foreign_toplevel_list_v1_types + 0 }, +}; + +static const struct wl_message ext_foreign_toplevel_handle_v1_events[] = { + { "closed", "", ext_foreign_toplevel_list_v1_types + 0 }, + { "done", "", ext_foreign_toplevel_list_v1_types + 0 }, + { "title", "s", ext_foreign_toplevel_list_v1_types + 0 }, + { "app_id", "s", ext_foreign_toplevel_list_v1_types + 0 }, + { "identifier", "s", ext_foreign_toplevel_list_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface ext_foreign_toplevel_handle_v1_interface = { + "ext_foreign_toplevel_handle_v1", 1, + 1, ext_foreign_toplevel_handle_v1_requests, + 5, ext_foreign_toplevel_handle_v1_events, +}; +
--- a/dep/animia/src/win/x11.cc Sun Dec 24 02:59:42 2023 -0500 +++ b/dep/animia/src/win/x11.cc Tue Jan 02 02:34:27 2024 -0500 @@ -5,6 +5,9 @@ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> // XA_* +#ifdef HAVE_XRES +#include <X11/extensions/XRes.h> +#endif #include <cstdint> #include <string> @@ -16,11 +19,16 @@ namespace animia::internal::x11 { +/* specify that these are X types. */ +typedef ::Window XWindow; +typedef ::Display XDisplay; +typedef ::Atom XAtom; + /* should return UTF8_STRING or STRING */ -static bool GetWindowPropertyAsString(::Display* display, ::Window window, ::Atom atom, std::string& result, ::Atom reqtype = AnyPropertyType) { +static bool GetWindowPropertyAsString(XDisplay* display, XWindow window, XAtom atom, std::string& result, XAtom reqtype = AnyPropertyType) { int format; unsigned long leftover_bytes, num_of_items; - ::Atom type; + XAtom type; unsigned char* data; int status = ::XGetWindowProperty(display, window, atom, 0L, (~0L), False, reqtype, @@ -36,10 +44,35 @@ } /* this should return CARDINAL, a 32-bit integer */ -static bool GetWindowPID(::Display* display, ::Window window, pid_t& result) { +static bool GetWindowPID(XDisplay* display, XWindow window, pid_t& result) { +#ifdef HAVE_XRES + { + long num_ids; + XResClientIdValue *client_ids; + XResClientIdSpec spec = { + .client = window, + .mask = XRES_CLIENT_ID_PID_MASK + }; + + ::XResQueryClientIds(display, 1, &spec, &num_ids, &client_ids); + + for (long i = 0; i < num_ids; i++) { + if (client_ids[i].spec.mask == XRES_CLIENT_ID_PID_MASK) { + result = ::XResGetClientPid(&client_ids[i]); + ::XResClientIdsDestroy(num_ids, client_ids); + return true; + } + } + + ::XResClientIdsDestroy(num_ids, client_ids); + + return false; + } +#endif + int format; unsigned long leftover_bytes, num_of_items; - ::Atom atom = ::XInternAtom(display, "_NET_WM_PID", False), type; + XAtom atom = ::XInternAtom(display, "_NET_WM_PID", False), type; unsigned char* data; int status = ::XGetWindowProperty(display, window, atom, 0L, (~0L), False, XA_CARDINAL, @@ -47,14 +80,14 @@ if (status != Success || type != XA_CARDINAL || num_of_items < 1) return false; - result = static_cast<pid_t>(*(uint32_t*)data); + result = static_cast<pid_t>(*reinterpret_cast<uint32_t*>(data)); ::XFree(data); return true; } -static bool FetchName(::Display* display, ::Window window, std::string& result) { +static bool FetchName(XDisplay* display, XWindow window, std::string& result) { /* TODO: Check if XInternAtom created None or not... */ if (GetWindowPropertyAsString(display, window, ::XInternAtom(display, "_NET_WM_NAME", False), result, ::XInternAtom(display, "UTF8_STRING", False))) @@ -92,19 +125,17 @@ return true; } -static bool WalkWindows(::Display* display, std::set<::Window>& children, const std::set<::Window>& windows) { - /* This sucks. It takes waaaay too long to finish. - * TODO: Look at the code for xwininfo to see what they do. - */ +static bool WalkWindows(XDisplay* display, std::set<XWindow>& children, const std::set<XWindow>& windows) { + /* This can take a VERY long time if many windows are open. */ if (windows.empty()) return false; - for (const ::Window& window : windows) { + for (const XWindow& window : windows) { unsigned int num_children = 0; - ::Window* children_arr = nullptr; + XWindow* children_arr = nullptr; - ::Window root_return; - ::Window parent_return; + XWindow root_return; + XWindow parent_return; int status = ::XQueryTree(display, window, &root_return, &parent_return, &children_arr, &num_children); if (!status || !children_arr) @@ -121,7 +152,7 @@ ::XFree(children_arr); - std::set<::Window> children_children; + std::set<XWindow> children_children; if (WalkWindows(display, children_children, children)) children.insert(children_children.begin(), children_children.end()); @@ -134,13 +165,13 @@ if (!window_proc) return false; - ::Display* display = ::XOpenDisplay(nullptr); + XDisplay* display = ::XOpenDisplay(nullptr); if (!display) return false; - ::Window root = DefaultRootWindow(display); + XWindow root = ::XDefaultRootWindow(display); - std::set<::Window> windows; + std::set<XWindow> windows; WalkWindows(display, windows, {root}); for (const auto& window : windows) {