aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/recorder/windowing.c
blob: 0bd1de6dd2cca229dd34ad5cfad8925571151cb8 (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#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;
}

void gsr_windowing_deinit(gsr_windowing *self) {
    if(self->egl_loaded) {
        gsr_egl_unload(&self->egl);
        self->egl_loaded = false;
    }

    if(self->window) {
        gsr_window_destroy(self->window);
        self->window = NULL;
    }

    if(self->display) {
        /* TODO: XCloseDisplay causes a crash, why? maybe some other library dlclose xlib and that also happened to unload this??? */
        //XCloseDisplay(self->display);
        self->display = NULL;
    }
}

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