diff options
-rwxr-xr-x | build.sh | 9 | ||||
-rw-r--r-- | external/wlr-export-dmabuf-unstable-v1.xml | 203 | ||||
-rw-r--r-- | src/egl.c | 20 | ||||
-rw-r--r-- | src/egl.h | 6 | ||||
-rw-r--r-- | src/main.cpp | 60 |
5 files changed, 24 insertions, 274 deletions
@@ -9,22 +9,15 @@ CXX=${CXX:-g++} opts="-O2 -g0 -DNDEBUG -Wall -Wextra -Werror -s" [ -n "$DEBUG" ] && opts="-O0 -g3 -Wall -Wextra -Werror"; -build_wayland_protocol() { - wayland-scanner private-code external/wlr-export-dmabuf-unstable-v1.xml external/wlr-export-dmabuf-unstable-v1-protocol.c - wayland-scanner client-header external/wlr-export-dmabuf-unstable-v1.xml external/wlr-export-dmabuf-unstable-v1-client-protocol.h -} - build_gsr_gtk() { dependencies="gtk+-3.0 x11 xrandr libpulse libcap libdrm wayland-egl wayland-client" includes="$(pkg-config --cflags $dependencies)" libs="$(pkg-config --libs $dependencies) -ldl" $CC -c src/egl.c $opts $includes $CC -c src/library_loader.c $opts $includes - $CC -c external/wlr-export-dmabuf-unstable-v1-protocol.c $opts $includes $CXX -c src/main.cpp $opts $includes - $CXX -o gpu-screen-recorder-gtk egl.o library_loader.o wlr-export-dmabuf-unstable-v1-protocol.o main.o $libs $opts + $CXX -o gpu-screen-recorder-gtk egl.o library_loader.o main.o $libs $opts } -build_wayland_protocol build_gsr_gtk echo "Successfully built gpu-screen-recorder-gtk" diff --git a/external/wlr-export-dmabuf-unstable-v1.xml b/external/wlr-export-dmabuf-unstable-v1.xml deleted file mode 100644 index 2614065..0000000 --- a/external/wlr-export-dmabuf-unstable-v1.xml +++ /dev/null @@ -1,203 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<protocol name="wlr_export_dmabuf_unstable_v1"> - <copyright> - Copyright © 2018 Rostislav Pehlivanov - - 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="a protocol for low overhead screen content capturing"> - An interface to capture surfaces in an efficient way by exporting DMA-BUFs. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - </description> - - <interface name="zwlr_export_dmabuf_manager_v1" version="1"> - <description summary="manager to inform clients and begin capturing"> - This object is a manager with which to start capturing from sources. - </description> - - <request name="capture_output"> - <description summary="capture a frame from an output"> - Capture the next frame of a an entire output. - </description> - <arg name="frame" type="new_id" interface="zwlr_export_dmabuf_frame_v1"/> - <arg name="overlay_cursor" type="int" - summary="include custom client hardware cursor on top of the frame"/> - <arg name="output" type="object" interface="wl_output"/> - </request> - - <request name="destroy" type="destructor"> - <description summary="destroy the manager"> - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - </description> - </request> - </interface> - - <interface name="zwlr_export_dmabuf_frame_v1" version="1"> - <description summary="a DMA-BUF frame"> - This object represents a single DMA-BUF frame. - - If the capture is successful, the compositor will first send a "frame" - event, followed by one or several "object". When the frame is available - for readout, the "ready" event is sent. - - If the capture failed, the "cancel" event is sent. This can happen anytime - before the "ready" event. - - Once either a "ready" or a "cancel" event is received, the client should - destroy the frame. Once an "object" event is received, the client is - responsible for closing the associated file descriptor. - - All frames are read-only and may not be written into or altered. - </description> - - <enum name="flags"> - <description summary="frame flags"> - Special flags that should be respected by the client. - </description> - <entry name="transient" value="0x1" - summary="clients should copy frame before processing"/> - </enum> - - <event name="frame"> - <description summary="a frame description"> - Main event supplying the client with information about the frame. If the - capture didn't fail, this event is always emitted first before any other - events. - - This event is followed by a number of "object" as specified by the - "num_objects" argument. - </description> - <arg name="width" type="uint" - summary="frame width in pixels"/> - <arg name="height" type="uint" - summary="frame height in pixels"/> - <arg name="offset_x" type="uint" - summary="crop offset for the x axis"/> - <arg name="offset_y" type="uint" - summary="crop offset for the y axis"/> - <arg name="buffer_flags" type="uint" - summary="flags which indicate properties (invert, interlacing), - has the same values as zwp_linux_buffer_params_v1:flags"/> - <arg name="flags" type="uint" enum="flags" - summary="indicates special frame features"/> - <arg name="format" type="uint" - summary="format of the frame (DRM_FORMAT_*)"/> - <arg name="mod_high" type="uint" - summary="drm format modifier, high"/> - <arg name="mod_low" type="uint" - summary="drm format modifier, low"/> - <arg name="num_objects" type="uint" - summary="indicates how many objects (FDs) the frame has (max 4)"/> - </event> - - <event name="object"> - <description summary="an object description"> - Event which serves to supply the client with the file descriptors - containing the data for each object. - - After receiving this event, the client must always close the file - descriptor as soon as they're done with it and even if the frame fails. - </description> - <arg name="index" type="uint" - summary="index of the current object"/> - <arg name="fd" type="fd" - summary="fd of the current object"/> - <arg name="size" type="uint" - summary="size in bytes for the current object"/> - <arg name="offset" type="uint" - summary="starting point for the data in the object's fd"/> - <arg name="stride" type="uint" - summary="line size in bytes"/> - <arg name="plane_index" type="uint" - summary="index of the the plane the data in the object applies to"/> - </event> - - <event name="ready"> - <description summary="indicates frame is available for reading"> - This event is sent as soon as the frame is presented, indicating it is - available for reading. This event includes the time at which - presentation happened at. - - 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]. The seconds part - may have an arbitrary offset at start. - - After receiving this event, the client should destroy this object. - </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> - - <enum name="cancel_reason"> - <description summary="cancel reason"> - Indicates reason for cancelling the frame. - </description> - <entry name="temporary" value="0" - summary="temporary error, source will produce more frames"/> - <entry name="permanent" value="1" - summary="fatal error, source will not produce frames"/> - <entry name="resizing" value="2" - summary="temporary error, source will produce more frames"/> - </enum> - - <event name="cancel"> - <description summary="indicates the frame is no longer valid"> - If the capture failed or if the frame is no longer valid after the - "frame" event has been emitted, this event will be used to inform the - client to scrap the frame. - - If the failure is temporary, the client may capture again the same - source. If the failure is permanent, any further attempts to capture the - same source will fail again. - - After receiving this event, the client should destroy this object. - </description> - <arg name="reason" type="uint" enum="cancel_reason" - summary="indicates a reason for cancelling this frame capture"/> - </event> - - <request name="destroy" type="destructor"> - <description summary="delete this object, used or not"> - Unreferences the frame. This request must be called as soon as its no - longer used. - - It can be called at any time by the client. The client will still have - to close any FDs it has been given. - </description> - </request> - </interface> -</protocol>
\ No newline at end of file @@ -8,7 +8,6 @@ #include <wayland-client.h> #include <wayland-egl.h> -#include "../external/wlr-export-dmabuf-unstable-v1-client-protocol.h" #include <unistd.h> static void output_handle_geometry(void *data, struct wl_output *wl_output, @@ -102,12 +101,6 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32 .name = NULL, }; wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); - } else if(strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) { - if(egl->wayland.export_manager) { - zwlr_export_dmabuf_manager_v1_destroy(egl->wayland.export_manager); - egl->wayland.export_manager = NULL; - } - egl->wayland.export_manager = wl_registry_bind(registry, name, &zwlr_export_dmabuf_manager_v1_interface, 1); } } @@ -318,11 +311,6 @@ void gsr_egl_unload(gsr_egl *self) { self->x11.window = None; } - if(self->wayland.export_manager) { - zwlr_export_dmabuf_manager_v1_destroy(self->wayland.export_manager); - self->wayland.export_manager = NULL; - } - if(self->wayland.window) { wl_egl_window_destroy(self->wayland.window); self->wayland.window = NULL; @@ -373,11 +361,3 @@ void gsr_egl_unload(gsr_egl *self) { memset(self, 0, sizeof(gsr_egl)); } - -bool gsr_egl_supports_wayland_capture(gsr_egl *self) { - // TODO: wlroots capture is broken right now (black screen) on amd and multiple monitors - // so it has to be disabled right now. Find out why it happens and fix it. - (void)self; - return false; - //return !!self->wayland.export_manager && self->wayland.num_outputs > 0; -} @@ -68,10 +68,8 @@ typedef struct { void *registry; void *surface; void *compositor; - void *export_manager; gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; int num_outputs; - gsr_wayland_output *output_to_capture; } gsr_wayland; typedef struct { @@ -84,6 +82,7 @@ typedef struct { gsr_x11 x11; gsr_wayland wayland; + char card_path[128]; EGLDisplay (*eglGetDisplay)(EGLNativeDisplayType display_id); unsigned int (*eglInitialize)(EGLDisplay dpy, int32_t *major, int32_t *minor); @@ -102,7 +101,4 @@ typedef struct { bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland); void gsr_egl_unload(gsr_egl *self); -/* wayland protocol capture, does not include kms capture */ -bool gsr_egl_supports_wayland_capture(gsr_egl *self); - #endif /* GSR_EGL_H */ diff --git a/src/main.cpp b/src/main.cpp index 6d89798..8cde7df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -117,7 +117,6 @@ static std::string record_file_current_filename; static bool nvfbc_installed = false; static bool wayland = false; -static char drm_card_path[128]; static gsr_egl egl; struct AudioInput { @@ -664,7 +663,7 @@ static const XRRModeInfo* get_mode_info(const XRRScreenResources *sr, RRMode id) return NULL; } -static void for_each_active_monitor_output_x11(Display *display, active_monitor_callback &callback, void *userdata) { +static void for_each_active_monitor_output_x11(Display *display, active_monitor_callback callback, void *userdata) { XRRScreenResources *screen_res = XRRGetScreenResources(display, DefaultRootWindow(display)); if(!screen_res) return; @@ -738,10 +737,7 @@ static bool connector_get_property_by_name(int drmfd, drmModeConnectorPtr props, return false; } -static void for_each_active_monitor_output_wayland(gsr_egl *egl, active_monitor_callback callback, void *userdata) { - if(!gsr_egl_supports_wayland_capture(egl)) - return; - +static void for_each_active_monitor_output_wayland(const gsr_egl *egl, active_monitor_callback callback, void *userdata) { for(int i = 0; i < egl->wayland.num_outputs; ++i) { if(!egl->wayland.outputs[i].name) continue; @@ -757,8 +753,8 @@ static void for_each_active_monitor_output_wayland(gsr_egl *egl, active_monitor_ } } -static void for_each_active_monitor_output_drm(const char *drm_card_path, active_monitor_callback callback, void *userdata) { - int fd = open(drm_card_path, O_RDONLY); +static void for_each_active_monitor_output_drm(const gsr_egl *egl, active_monitor_callback callback, void *userdata) { + int fd = open(egl->card_path, O_RDONLY); if(fd == -1) return; @@ -813,16 +809,16 @@ static void for_each_active_monitor_output_drm(const char *drm_card_path, active close(fd); } -static void for_each_active_monitor_output(void *connection, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata) { +static void for_each_active_monitor_output(const gsr_egl *egl, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata) { switch(connection_type) { case GSR_CONNECTION_X11: - for_each_active_monitor_output_x11((Display*)connection, callback, userdata); + for_each_active_monitor_output_x11(egl->x11.dpy, callback, userdata); break; case GSR_CONNECTION_WAYLAND: - for_each_active_monitor_output_wayland((gsr_egl*)connection, callback, userdata); + for_each_active_monitor_output_wayland(egl, callback, userdata); break; case GSR_CONNECTION_DRM: - for_each_active_monitor_output_drm((const char*)connection, callback, userdata); + for_each_active_monitor_output_drm(egl, callback, userdata); break; } } @@ -1293,7 +1289,7 @@ static HotkeyResult replace_grabbed_keys_depending_on_active_page() { static bool show_pkexec_flatpak_error_if_needed() { const std::string window_str = record_area_selection_menu_get_active_id(); - if(!gsr_egl_supports_wayland_capture(&egl) && (wayland || gpu_inf.vendor != GPU_VENDOR_NVIDIA) && window_str != "window" && window_str != "focused") { + if((wayland || gpu_inf.vendor != GPU_VENDOR_NVIDIA) && window_str != "window" && window_str != "focused") { if(!is_pkexec_installed()) { GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "pkexec needs to be installed to record a monitor with an AMD/Intel GPU. Please install and run polkit. Alternatively, record a single window which doesn't require root access."); @@ -2153,18 +2149,11 @@ static bool audio_inputs_contains(const std::vector<AudioInput> &audio_inputs, c return false; } -static void get_connection_by_active_type(void **connection, gsr_connection_type *connection_type) { +static gsr_connection_type get_connection_type() { if(wayland || gpu_inf.vendor != GPU_VENDOR_NVIDIA) { - if(gsr_egl_supports_wayland_capture(&egl)) { - *connection = &egl; - *connection_type = GSR_CONNECTION_WAYLAND; - } else { - *connection = (void*)drm_card_path; - *connection_type = GSR_CONNECTION_DRM; - } + return GSR_CONNECTION_DRM; } else { - *connection = gdk_x11_get_default_xdisplay(); - *connection_type = GSR_CONNECTION_X11; + return GSR_CONNECTION_X11; } } @@ -2284,7 +2273,7 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a gtk_combo_box_set_active(GTK_COMBO_BOX(view_combo_box), 0); g_signal_connect(view_combo_box, "changed", G_CALLBACK(view_combo_box_change_callback), view_combo_box); - if(is_inside_flatpak() && flatpak_is_installed_as_system() && drm_card_path[0] != '\0') { + if(is_inside_flatpak() && flatpak_is_installed_as_system() && egl.card_path[0] != '\0') { GtkGrid *password_prompt_grid = GTK_GRID(gtk_grid_new()); gtk_grid_attach(grid, GTK_WIDGET(password_prompt_grid), 0, grid_row++, 2, 1); gtk_grid_set_column_spacing(password_prompt_grid, 10); @@ -2344,11 +2333,9 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a gtk_list_store_set(store, &iter, 1, "screen", -1); } - void *connection = NULL; - gsr_connection_type connection_type = GSR_CONNECTION_DRM; - get_connection_by_active_type(&connection, &connection_type); + gsr_connection_type connection_type = get_connection_type(); - for_each_active_monitor_output(connection, connection_type, [&](const gsr_monitor *monitor, void*) { + for_each_active_monitor_output(&egl, connection_type, [&](const gsr_monitor *monitor, void*) { std::string label = "Monitor "; label.append(monitor->name, monitor->name_len); label += " ("; @@ -2356,8 +2343,7 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a label += "x"; label += std::to_string(monitor->size.y); if(wayland) { - if(!gsr_egl_supports_wayland_capture(&egl)) - label += ", requires root access"; + label += ", requires root access"; } else if(gpu_inf.vendor != GPU_VENDOR_NVIDIA) { label += ", requires root access"; } @@ -3046,13 +3032,11 @@ static void load_config(const gpu_info &gpu_inf) { } else if(!wayland && gpu_inf.vendor == GPU_VENDOR_NVIDIA && strcmp(config.main_config.record_area_option.c_str(), "screen") == 0) { // } else { - void *connection = NULL; - gsr_connection_type connection_type = GSR_CONNECTION_DRM; - get_connection_by_active_type(&connection, &connection_type); + gsr_connection_type connection_type = get_connection_type(); bool found_monitor = false; int monitor_name_size = strlen(config.main_config.record_area_option.c_str()); - for_each_active_monitor_output(connection, connection_type, [&](const gsr_monitor *monitor, void*) { + for_each_active_monitor_output(&egl, connection_type, [&](const gsr_monitor *monitor, void*) { if(first_monitor.empty()) { first_monitor.assign(monitor->name, monitor->name_len); } @@ -3265,7 +3249,7 @@ static bool is_xwayland(Display *dpy) { return true; bool xwayland_found = false; - for_each_active_monitor_output(dpy, GSR_CONNECTION_X11, [&xwayland_found](const gsr_monitor *monitor, void*) { + for_each_active_monitor_output_x11(dpy, [&xwayland_found](const gsr_monitor *monitor, void*) { if(monitor->name_len >= 8 && strncmp(monitor->name, "XWAYLAND", 8) == 0) xwayland_found = true; else if(memmem(monitor->name, monitor->name_len, "X11", 3)) @@ -3315,8 +3299,8 @@ static void activate(GtkApplication *app, gpointer) { return; } - if((gpu_inf.vendor != GPU_VENDOR_NVIDIA) || (wayland && !gsr_egl_supports_wayland_capture(&egl))) { - if(!gsr_get_valid_card_path(drm_card_path)) { + if((gpu_inf.vendor != GPU_VENDOR_NVIDIA) || wayland) { + if(!gsr_get_valid_card_path(egl.card_path)) { GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Failed to find a valid DRM card."); gtk_dialog_run(GTK_DIALOG(dialog)); @@ -3325,7 +3309,7 @@ static void activate(GtkApplication *app, gpointer) { return; } } else { - drm_card_path[0] = '\0'; + egl.card_path[0] = '\0'; } if(gpu_inf.vendor == GPU_VENDOR_NVIDIA) { |