aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/recorder/windowing.c
blob: 6671898ebbeb5941248395d73e9dee5170eb16c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "../../include/recorder/windowing.h"
#include "../../include/recorder/error.h"
#include "../../include/window/x11.h"
#include "../../include/window/wayland.h"
#include "../../include/utils.h"
#include "../../include/log.h"

#include <string.h>
#include <stdlib.h>

static int x11_error_handler(Display *display, XErrorEvent *event) {
    (void)display;
    (void)event;
    return 0;
}

static int x11_io_error_handler(Display *display) {
    (void)display;
    return 0;
}

static void xwayland_check_callback(const gsr_monitor *monitor, void *userdata) {
    bool *xwayland_found = (bool*)userdata;
    if(monitor->name_len >= 8 && strncmp(monitor->name, "XWAYLAND", 8) == 0)
        *xwayland_found = true;
    else if(memmem(monitor->name, monitor->name_len, "X11", 3))
        *xwayland_found = true;
}

static bool is_xwayland(Display *display) {
    int opcode, event, error;
    if(XQueryExtension(display, "XWAYLAND", &opcode, &event, &error))
        return true;

    bool xwayland_found = false;
    for_each_active_monitor_output_x11_not_cached(display, xwayland_check_callback, &xwayland_found);
    return xwayland_found;
}

bool gsr_windowing_is_using_prime_run(void) {
    const char *prime_render_offload = getenv("__NV_PRIME_RENDER_OFFLOAD");
    return (prime_render_offload && strcmp(prime_render_offload, "1") == 0) || getenv("DRI_PRIME");
}

void gsr_windowing_disable_prime_run(void) {
    unsetenv("__NV_PRIME_RENDER_OFFLOAD");
    unsetenv("__NV_PRIME_RENDER_OFFLOAD_PROVIDER");
    unsetenv("__GLX_VENDOR_LIBRARY_NAME");
    unsetenv("__VK_LAYER_NV_optimus");
    unsetenv("DRI_PRIME");
}

static gsr_window* window_create(Display *display, bool wayland) {
    if(wayland)
        return gsr_window_wayland_create();
    else
        return gsr_window_x11_create(display);
}

bool monitor_capture_use_drm(const gsr_window *window, gsr_gpu_vendor vendor) {
    return gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_WAYLAND || vendor != GSR_GPU_VENDOR_NVIDIA;
}

int gsr_windowing_init(gsr_windowing *self, const gsr_windowing_params *params) {
    memset(self, 0, sizeof(*self));
    self->card_path_found = true;

    bool wayland = false;
    self->display = XOpenDisplay(NULL);
    if(self->display) {
        if(params->listen_to_x11_events)
            XSelectInput(self->display, DefaultRootWindow(self->display), PropertyChangeMask);
    } else {
        wayland = true;
        gsr_log(GSR_LOG_LEVEL_WARNING, "failed to connect to the X server. Assuming wayland is running without Xwayland");
    }

    XSetErrorHandler(x11_error_handler);
    XSetIOErrorHandler(x11_io_error_handler);

    if(!wayland)
        wayland = is_xwayland(self->display);

    if(!wayland && gsr_windowing_is_using_prime_run()) {
        // Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
        // This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected
        // to the drm device.
        gsr_log(GSR_LOG_LEVEL_WARNING, "use of prime-run on X11 is not supported. Disabling prime-run");
        gsr_windowing_disable_prime_run();
    }

    self->window = window_create(self->display, wayland);
    if(!self->window) {
        gsr_log(GSR_LOG_LEVEL_ERROR, "failed to create window");
        return GSR_ERROR_GENERIC;
    }

    return GSR_ERROR_OK;
}

int gsr_windowing_load_egl(gsr_windowing *self, const gsr_windowing_params *params) {
    if(!gsr_egl_load(&self->egl, self->window, params->monitor_capture, params->gl_debug)) {
        gsr_log(GSR_LOG_LEVEL_ERROR, "failed to load opengl");
        return GSR_ERROR_OPENGL_LOAD_FAILED;
    }
    self->egl_loaded = true;

    self->egl.card_path[0] = '\0';
    if(monitor_capture_use_drm(self->window, self->egl.gpu_info.vendor)) {
        // TODO: Allow specifying another card, and in other places
        if(!gsr_get_valid_card_path(&self->egl, self->egl.card_path, params->monitor_capture))
            self->card_path_found = false;
    } else {
        gsr_get_valid_card_path(&self->egl, self->egl.card_path, false);
    }

    return GSR_ERROR_OK;
}

bool gsr_windowing_is_wayland(const gsr_windowing *self) {
    return gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_WAYLAND;
}