mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
Attempt to fix incorrect hdr colors on kde plasma 6.2
This commit is contained in:
@@ -179,7 +179,4 @@ You have to either record in hdr mode (-k `hevc_hdr` or -k `av1_hdr` option) to
|
|||||||
## GPU Screen Recorder records night light when recording in HDR mode
|
## GPU Screen Recorder records night light when recording in HDR mode
|
||||||
You can record with desktop portal option (`-w portal`) instead which ignores night light, if you are ok with recording without HDR.
|
You can record with desktop portal option (`-w portal`) instead which ignores night light, if you are ok with recording without HDR.
|
||||||
## Kdenlive says that the video is not usable for editing because it has variable frame rate
|
## Kdenlive says that the video is not usable for editing because it has variable frame rate
|
||||||
To fix this you can either record the video in .mkv format or constant frame rate (-fm cfr).
|
To fix this you can either record the video in .mkv format or constant frame rate (-fm cfr).
|
||||||
## Colors look incorrect when recording HDR (with hevc_hdr/av1_hdr) or using an ICC profile
|
|
||||||
The latest version of KDE Plasma breaks HDR and ICC profiles for screen recorders. Wayland in general doesn't properly support recording HDR yet. Use desktop portal option (`-w portal`) for now to turn HDR recording into SDR and to be able to record with correct colors when using an ICC profile.\
|
|
||||||
Note that this appears to only be an issue when trying to record SDR content. If you for example record a fullscreen HDR game then it appears to record correctly.
|
|
||||||
3
TODO
3
TODO
@@ -194,8 +194,7 @@ Add option to record audio from the recorded window only.
|
|||||||
|
|
||||||
Add option to automatically select best video codec available. Add -k best, -k best_10bit and -k best_hdr.
|
Add option to automatically select best video codec available. Add -k best, -k best_10bit and -k best_hdr.
|
||||||
|
|
||||||
HDR is broken on kde plasma > 6.2 because of change to how HDR metadata works. See https://github.com/dec05eba/gpu-screen-recorder-issues/issues/60.
|
Use wayland color management protocol when it's available: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/14.
|
||||||
Use wayland color management protocol when it's available: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/14.
|
|
||||||
|
|
||||||
Use different exit codes for different errors. Use one for invalid -w option, another one for invalid -a option for audio devices, etc. This is to make UI error reporting better.
|
Use different exit codes for different errors. Use one for invalid -w option, another one for invalid -a option for audio devices, etc. This is to make UI error reporting better.
|
||||||
Document these exit codes in an exit code .md file, or finally create a manpage where this can be documented.
|
Document these exit codes in an exit code .md file, or finally create a manpage where this can be documented.
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ typedef struct {
|
|||||||
|
|
||||||
gsr_color_range color_range;
|
gsr_color_range color_range;
|
||||||
bool load_external_image_shader;
|
bool load_external_image_shader;
|
||||||
|
|
||||||
|
bool kde_gamma_correction;
|
||||||
} gsr_color_conversion_params;
|
} gsr_color_conversion_params;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count
|
|||||||
uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count);
|
uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count);
|
||||||
|
|
||||||
bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info);
|
bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info);
|
||||||
|
bool version_greater_than(int major, int minor, int patch, int other_major, int other_minor, int other_patch);
|
||||||
bool gl_driver_version_greater_than(const gsr_gpu_info *gpu_info, int major, int minor, int patch);
|
bool gl_driver_version_greater_than(const gsr_gpu_info *gpu_info, int major, int minor, int patch);
|
||||||
|
|
||||||
bool try_card_has_valid_plane(const char *card_path);
|
bool try_card_has_valid_plane(const char *card_path);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ struct gsr_window {
|
|||||||
void* (*get_display)(gsr_window *self);
|
void* (*get_display)(gsr_window *self);
|
||||||
void* (*get_window)(gsr_window *self);
|
void* (*get_window)(gsr_window *self);
|
||||||
void (*for_each_active_monitor_output_cached)(const gsr_window *self, active_monitor_callback callback, void *userdata);
|
void (*for_each_active_monitor_output_cached)(const gsr_window *self, active_monitor_callback callback, void *userdata);
|
||||||
|
bool (*is_compositor_kwin)(const gsr_window *self); /* can be NULL. Is currently only defined for Wayland */
|
||||||
void *priv;
|
void *priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -236,6 +236,11 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c
|
|||||||
if(self->fast_path_failed)
|
if(self->fast_path_failed)
|
||||||
fprintf(stderr, "gsr warning: gsr_capture_kms_start: your amd driver (mesa) version is known to be buggy (<= version 24.0.9), falling back to opengl copy\n");
|
fprintf(stderr, "gsr warning: gsr_capture_kms_start: your amd driver (mesa) version is known to be buggy (<= version 24.0.9), falling back to opengl copy\n");
|
||||||
|
|
||||||
|
if(self->params.hdr) {
|
||||||
|
self->fast_path_failed = true;
|
||||||
|
fprintf(stderr, "gsr warning: gsr_capture_kms_start: recording with hdr requires shader color conversion which might be slow. If this is an issue record with -w portal instead (which converts HDR to SDR)\n");
|
||||||
|
}
|
||||||
|
|
||||||
self->mesa_supports_compute_only_vaapi_copy = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 3, 6);
|
self->mesa_supports_compute_only_vaapi_copy = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 3, 6);
|
||||||
|
|
||||||
frame->width = video_codec_context->width;
|
frame->width = video_codec_context->width;
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ static const char* color_format_range_get_transform_matrix(gsr_destination_color
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) {
|
static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool kde_gamma_correction) {
|
||||||
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
|
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
|
||||||
|
|
||||||
char vertex_shader[2048];
|
char vertex_shader[2048];
|
||||||
@@ -93,6 +93,19 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u
|
|||||||
" gl_Position = vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0); \n"
|
" gl_Position = vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0); \n"
|
||||||
"} \n");
|
"} \n");
|
||||||
|
|
||||||
|
const char *main_code = NULL;
|
||||||
|
if(kde_gamma_correction) {
|
||||||
|
main_code =
|
||||||
|
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
||||||
|
" FragColor.x = pow((RGBtoYUV * vec4(pixel.rgb, 1.0)).x, 0.55)*0.8; \n"
|
||||||
|
" FragColor.w = pixel.a; \n";
|
||||||
|
} else {
|
||||||
|
main_code =
|
||||||
|
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
||||||
|
" FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n"
|
||||||
|
" FragColor.w = pixel.a; \n";
|
||||||
|
}
|
||||||
|
|
||||||
char fragment_shader[2048];
|
char fragment_shader[2048];
|
||||||
if(external_texture) {
|
if(external_texture) {
|
||||||
snprintf(fragment_shader, sizeof(fragment_shader),
|
snprintf(fragment_shader, sizeof(fragment_shader),
|
||||||
@@ -106,10 +119,8 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u
|
|||||||
"%s"
|
"%s"
|
||||||
"void main() \n"
|
"void main() \n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
"%s"
|
||||||
" FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n"
|
"} \n", color_transform_matrix, main_code);
|
||||||
" FragColor.w = pixel.a; \n"
|
|
||||||
"} \n", color_transform_matrix);
|
|
||||||
} else {
|
} else {
|
||||||
snprintf(fragment_shader, sizeof(fragment_shader),
|
snprintf(fragment_shader, sizeof(fragment_shader),
|
||||||
"#version 300 es \n"
|
"#version 300 es \n"
|
||||||
@@ -120,10 +131,8 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u
|
|||||||
"%s"
|
"%s"
|
||||||
"void main() \n"
|
"void main() \n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
"%s"
|
||||||
" FragColor.x = (RGBtoYUV * vec4(pixel.rgb, 1.0)).x; \n"
|
"} \n", color_transform_matrix, main_code);
|
||||||
" FragColor.w = pixel.a; \n"
|
|
||||||
"} \n", color_transform_matrix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
|
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
|
||||||
@@ -136,7 +145,7 @@ static int load_shader_y(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *u
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture) {
|
static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_uniforms *uniforms, gsr_destination_color color_format, gsr_color_range color_range, bool external_texture, bool kde_gamma_correction) {
|
||||||
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
|
const char *color_transform_matrix = color_format_range_get_transform_matrix(color_format, color_range);
|
||||||
|
|
||||||
char vertex_shader[2048];
|
char vertex_shader[2048];
|
||||||
@@ -154,6 +163,19 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_u
|
|||||||
" gl_Position = (vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0)) * vec4(0.5, 0.5, 1.0, 1.0) - vec4(0.5, 0.5, 0.0, 0.0); \n"
|
" gl_Position = (vec4(offset.x, offset.y, 0.0, 0.0) + vec4(pos.x, pos.y, 0.0, 1.0)) * vec4(0.5, 0.5, 1.0, 1.0) - vec4(0.5, 0.5, 0.0, 0.0); \n"
|
||||||
"} \n");
|
"} \n");
|
||||||
|
|
||||||
|
const char *main_code = NULL;
|
||||||
|
if(kde_gamma_correction) {
|
||||||
|
main_code =
|
||||||
|
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
||||||
|
" FragColor.xy = (RGBtoYUV * vec4(pow(pixel.rgb, vec3(0.3)), 1.0)).yz; \n"
|
||||||
|
" FragColor.w = pixel.a; \n";
|
||||||
|
} else {
|
||||||
|
main_code =
|
||||||
|
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
||||||
|
" FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n"
|
||||||
|
" FragColor.w = pixel.a; \n";
|
||||||
|
}
|
||||||
|
|
||||||
char fragment_shader[2048];
|
char fragment_shader[2048];
|
||||||
if(external_texture) {
|
if(external_texture) {
|
||||||
snprintf(fragment_shader, sizeof(fragment_shader),
|
snprintf(fragment_shader, sizeof(fragment_shader),
|
||||||
@@ -167,10 +189,8 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_u
|
|||||||
"%s"
|
"%s"
|
||||||
"void main() \n"
|
"void main() \n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
"%s"
|
||||||
" FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n"
|
"} \n", color_transform_matrix, main_code);
|
||||||
" FragColor.w = pixel.a; \n"
|
|
||||||
"} \n", color_transform_matrix);
|
|
||||||
} else {
|
} else {
|
||||||
snprintf(fragment_shader, sizeof(fragment_shader),
|
snprintf(fragment_shader, sizeof(fragment_shader),
|
||||||
"#version 300 es \n"
|
"#version 300 es \n"
|
||||||
@@ -181,10 +201,8 @@ static unsigned int load_shader_uv(gsr_shader *shader, gsr_egl *egl, gsr_color_u
|
|||||||
"%s"
|
"%s"
|
||||||
"void main() \n"
|
"void main() \n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" vec4 pixel = texture(tex1, texcoords_out); \n"
|
"%s"
|
||||||
" FragColor.xy = (RGBtoYUV * vec4(pixel.rgb, 1.0)).yz; \n"
|
"} \n", color_transform_matrix, main_code);
|
||||||
" FragColor.w = pixel.a; \n"
|
|
||||||
"} \n", color_transform_matrix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
|
if(gsr_shader_init(shader, egl, vertex_shader, fragment_shader) != 0)
|
||||||
@@ -261,23 +279,23 @@ int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conver
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(load_shader_y(&self->shaders[0], self->params.egl, &self->uniforms[0], params->destination_color, params->color_range, false) != 0) {
|
if(load_shader_y(&self->shaders[0], self->params.egl, &self->uniforms[0], params->destination_color, params->color_range, false, params->kde_gamma_correction) != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n");
|
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(load_shader_uv(&self->shaders[1], self->params.egl, &self->uniforms[1], params->destination_color, params->color_range, false) != 0) {
|
if(load_shader_uv(&self->shaders[1], self->params.egl, &self->uniforms[1], params->destination_color, params->color_range, false, params->kde_gamma_correction) != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n");
|
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(self->params.load_external_image_shader) {
|
if(self->params.load_external_image_shader) {
|
||||||
if(load_shader_y(&self->shaders[2], self->params.egl, &self->uniforms[2], params->destination_color, params->color_range, true) != 0) {
|
if(load_shader_y(&self->shaders[2], self->params.egl, &self->uniforms[2], params->destination_color, params->color_range, true, params->kde_gamma_correction) != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n");
|
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load Y shader\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(load_shader_uv(&self->shaders[3], self->params.egl, &self->uniforms[3], params->destination_color, params->color_range, true) != 0) {
|
if(load_shader_uv(&self->shaders[3], self->params.egl, &self->uniforms[3], params->destination_color, params->color_range, true, params->kde_gamma_correction) != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n");
|
fprintf(stderr, "gsr error: gsr_color_conversion_init: failed to load UV shader\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/main.cpp
28
src/main.cpp
@@ -3006,6 +3006,33 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static bool is_kde_plasma_version_greater_than_6_1_90() {
|
||||||
|
FILE *f = popen("plasmashell -v 2> /dev/null", "r");
|
||||||
|
if(!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char output[512];
|
||||||
|
const ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f);
|
||||||
|
if(bytes_read < 0 || ferror(f)) {
|
||||||
|
pclose(f);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
output[bytes_read] = '\0';
|
||||||
|
|
||||||
|
bool is_above = false;
|
||||||
|
const char *version_start = strstr(output, "plasmashell ");
|
||||||
|
if(version_start) {
|
||||||
|
version_start += 12;
|
||||||
|
int major = 0;
|
||||||
|
int minor = 0;
|
||||||
|
int patch = 0;
|
||||||
|
is_above = sscanf(version_start, "%d.%d.%d", &major, &minor, &patch) == 3 && version_greater_than(major, minor, patch, 6, 1, 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(f);
|
||||||
|
return is_above;
|
||||||
|
}
|
||||||
|
|
||||||
static bool arg_get_boolean_value(std::map<std::string, Arg> &args, const char *arg_name, bool default_value) {
|
static bool arg_get_boolean_value(std::map<std::string, Arg> &args, const char *arg_name, bool default_value) {
|
||||||
auto it = args.find(arg_name);
|
auto it = args.find(arg_name);
|
||||||
if(it == args.end() || !it->second.value()) {
|
if(it == args.end() || !it->second.value()) {
|
||||||
@@ -3734,6 +3761,7 @@ int main(int argc, char **argv) {
|
|||||||
color_conversion_params.color_range = color_range;
|
color_conversion_params.color_range = color_range;
|
||||||
color_conversion_params.egl = &egl;
|
color_conversion_params.egl = &egl;
|
||||||
color_conversion_params.load_external_image_shader = gsr_capture_uses_external_image(capture);
|
color_conversion_params.load_external_image_shader = gsr_capture_uses_external_image(capture);
|
||||||
|
color_conversion_params.kde_gamma_correction = hdr && is_monitor_capture && window->is_compositor_kwin && window->is_compositor_kwin(window) && is_kde_plasma_version_greater_than_6_1_90();
|
||||||
gsr_video_encoder_get_textures(video_encoder, color_conversion_params.destination_textures, &color_conversion_params.num_destination_textures, &color_conversion_params.destination_color);
|
gsr_video_encoder_get_textures(video_encoder, color_conversion_params.destination_textures, &color_conversion_params.num_destination_textures, &color_conversion_params.destination_color);
|
||||||
|
|
||||||
gsr_color_conversion color_conversion;
|
gsr_color_conversion color_conversion;
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info) {
|
|||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool version_greater_than(int major, int minor, int patch, int other_major, int other_minor, int other_patch) {
|
bool version_greater_than(int major, int minor, int patch, int other_major, int other_minor, int other_patch) {
|
||||||
return (major > other_major) || (major == other_major && minor > other_minor) || (major == other_major && minor == other_minor && patch > other_patch);
|
return (major > other_major) || (major == other_major && minor > other_minor) || (major == other_major && minor == other_minor && patch > other_patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ typedef struct {
|
|||||||
void *compositor;
|
void *compositor;
|
||||||
gsr_wayland_output outputs[GSR_MAX_OUTPUTS];
|
gsr_wayland_output outputs[GSR_MAX_OUTPUTS];
|
||||||
int num_outputs;
|
int num_outputs;
|
||||||
|
bool is_compositor_kwin;
|
||||||
} gsr_window_wayland;
|
} gsr_window_wayland;
|
||||||
|
|
||||||
static void output_handle_geometry(void *data, struct wl_output *wl_output,
|
static void output_handle_geometry(void *data, struct wl_output *wl_output,
|
||||||
@@ -123,6 +124,8 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32
|
|||||||
.name = NULL,
|
.name = NULL,
|
||||||
};
|
};
|
||||||
wl_output_add_listener(gsr_output->output, &output_listener, gsr_output);
|
wl_output_add_listener(gsr_output->output, &output_listener, gsr_output);
|
||||||
|
} else if(strcmp(interface, "org_kde_plasma_shell") == 0) {
|
||||||
|
window_wayland->is_compositor_kwin = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +292,11 @@ static void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_w
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool gsr_window_wayland_is_compositor_kwin(const gsr_window *window) {
|
||||||
|
const gsr_window_wayland *self = window->priv;
|
||||||
|
return self->is_compositor_kwin;
|
||||||
|
}
|
||||||
|
|
||||||
gsr_window* gsr_window_wayland_create(void) {
|
gsr_window* gsr_window_wayland_create(void) {
|
||||||
gsr_window *window = calloc(1, sizeof(gsr_window));
|
gsr_window *window = calloc(1, sizeof(gsr_window));
|
||||||
if(!window)
|
if(!window)
|
||||||
@@ -314,6 +322,7 @@ gsr_window* gsr_window_wayland_create(void) {
|
|||||||
.get_display = gsr_window_wayland_get_display,
|
.get_display = gsr_window_wayland_get_display,
|
||||||
.get_window = gsr_window_wayland_get_window,
|
.get_window = gsr_window_wayland_get_window,
|
||||||
.for_each_active_monitor_output_cached = gsr_window_wayland_for_each_active_monitor_output_cached,
|
.for_each_active_monitor_output_cached = gsr_window_wayland_for_each_active_monitor_output_cached,
|
||||||
|
.is_compositor_kwin = gsr_window_wayland_is_compositor_kwin,
|
||||||
.priv = window_wayland
|
.priv = window_wayland
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user