mirror of
https://github.com/artizirk/wdisplays.git
synced 2024-06-26 19:10:12 +03:00
59edbbd155
The output name overlay renders on the top left by default. At the same time, wdisplays renders the output previews on its top left corner too. The results is that the overlay covered the output previews, making seeing it and interacting with it tricky. Moving the output name to the bottom left makes sure there's never any overlap between it and the draggable previews shows for each output.
197 lines
6.6 KiB
C
197 lines
6.6 KiB
C
/* SPDX-FileCopyrightText: 2020 Jason Francis <jason@cycles.network>
|
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
|
|
#define _GNU_SOURCE
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkwayland.h>
|
|
|
|
#include "wdisplays.h"
|
|
|
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
|
|
|
#define SCREEN_MARGIN_PERCENT 0.02
|
|
|
|
static void layer_surface_configure(void *data,
|
|
struct zwlr_layer_surface_v1 *surface,
|
|
uint32_t serial, uint32_t width, uint32_t height) {
|
|
struct wd_output *output = data;
|
|
gtk_widget_set_size_request(output->overlay_window, width, height);
|
|
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
|
}
|
|
|
|
static void layer_surface_closed(void *data,
|
|
struct zwlr_layer_surface_v1 *surface) {
|
|
}
|
|
|
|
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
|
.configure = layer_surface_configure,
|
|
.closed = layer_surface_closed,
|
|
};
|
|
|
|
static inline int min(int a, int b) {
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
static PangoLayout *create_text_layout(struct wd_head *head,
|
|
PangoContext *pango, GtkStyleContext *style) {
|
|
GtkStyleContext *desc_style = gtk_style_context_new();
|
|
gtk_style_context_set_screen(desc_style,
|
|
gtk_style_context_get_screen(style));
|
|
GtkWidgetPath *desc_path = gtk_widget_path_copy(
|
|
gtk_style_context_get_path(style));
|
|
gtk_widget_path_append_type(desc_path, G_TYPE_NONE);
|
|
gtk_style_context_set_path(desc_style, desc_path);
|
|
gtk_style_context_add_class(desc_style, "description");
|
|
|
|
double desc_font_size = 16.;
|
|
gtk_style_context_get(desc_style, GTK_STATE_FLAG_NORMAL,
|
|
"font-size", &desc_font_size, NULL);
|
|
|
|
g_autofree gchar *str = g_strdup_printf("%s\n<span size=\"%d\">%s</span>",
|
|
head->name, (int) (desc_font_size * PANGO_SCALE), head->description);
|
|
PangoLayout *layout = pango_layout_new(pango);
|
|
|
|
pango_layout_set_markup(layout, str, -1);
|
|
return layout;
|
|
}
|
|
|
|
static void resize(struct wd_output *output) {
|
|
struct wd_head *head = wd_find_head(output->state, output);
|
|
|
|
uint32_t screen_width = head->custom_mode.width;
|
|
uint32_t screen_height = head->custom_mode.height;
|
|
if (head->mode != NULL) {
|
|
screen_width = head->mode->width;
|
|
screen_height = head->mode->height;
|
|
}
|
|
uint32_t margin = min(screen_width, screen_height) * SCREEN_MARGIN_PERCENT;
|
|
|
|
GdkWindow *window = gtk_widget_get_window(output->overlay_window);
|
|
PangoContext *pango = gtk_widget_get_pango_context(output->overlay_window);
|
|
GtkStyleContext *style_ctx = gtk_widget_get_style_context(
|
|
output->overlay_window);
|
|
PangoLayout *layout = create_text_layout(head, pango, style_ctx);
|
|
|
|
int width;
|
|
int height;
|
|
pango_layout_get_pixel_size(layout, &width, &height);
|
|
g_object_unref(layout);
|
|
|
|
|
|
GtkBorder padding;
|
|
gtk_style_context_get_padding(style_ctx, GTK_STATE_FLAG_NORMAL, &padding);
|
|
|
|
width = min(width, screen_width - margin * 2)
|
|
+ padding.left + padding.right;
|
|
height = min(height, screen_height - margin * 2)
|
|
+ padding.top + padding.bottom;
|
|
|
|
zwlr_layer_surface_v1_set_margin(output->overlay_layer_surface,
|
|
margin, margin, margin, margin);
|
|
zwlr_layer_surface_v1_set_size(output->overlay_layer_surface,
|
|
width, height);
|
|
|
|
struct wl_surface *surface = gdk_wayland_window_get_wl_surface(window);
|
|
wl_surface_commit(surface);
|
|
|
|
GdkDisplay *display = gdk_window_get_display(window);
|
|
wl_display_roundtrip(gdk_wayland_display_get_wl_display(display));
|
|
}
|
|
|
|
void wd_redraw_overlay(struct wd_output *output) {
|
|
if (output->overlay_window != NULL) {
|
|
resize(output);
|
|
gtk_widget_queue_draw(output->overlay_window);
|
|
}
|
|
}
|
|
|
|
void window_realize(GtkWidget *widget, gpointer data) {
|
|
GdkWindow *window = gtk_widget_get_window(widget);
|
|
gdk_wayland_window_set_use_custom_surface(window);
|
|
}
|
|
|
|
void window_map(GtkWidget *widget, gpointer data) {
|
|
struct wd_output *output = data;
|
|
|
|
GdkWindow *window = gtk_widget_get_window(widget);
|
|
cairo_region_t *region = cairo_region_create();
|
|
gdk_window_input_shape_combine_region(window, region, 0, 0);
|
|
cairo_region_destroy(region);
|
|
|
|
struct wl_surface *surface = gdk_wayland_window_get_wl_surface(window);
|
|
|
|
output->overlay_layer_surface = zwlr_layer_shell_v1_get_layer_surface(
|
|
output->state->layer_shell, surface, output->wl_output,
|
|
ZWLR_LAYER_SHELL_V1_LAYER_TOP, "output-overlay");
|
|
|
|
zwlr_layer_surface_v1_add_listener(output->overlay_layer_surface,
|
|
&layer_surface_listener, output);
|
|
|
|
zwlr_layer_surface_v1_set_anchor(output->overlay_layer_surface,
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
|
|
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
|
|
|
|
resize(output);
|
|
}
|
|
|
|
void window_unmap(GtkWidget *widget, gpointer data) {
|
|
struct wd_output *output = data;
|
|
zwlr_layer_surface_v1_destroy(output->overlay_layer_surface);
|
|
}
|
|
|
|
gboolean window_draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
|
struct wd_output *output = data;
|
|
struct wd_head *head = wd_find_head(output->state, output);
|
|
|
|
GtkStyleContext *style_ctx = gtk_widget_get_style_context(widget);
|
|
GdkRGBA fg;
|
|
gtk_style_context_get_color(style_ctx, GTK_STATE_FLAG_NORMAL, &fg);
|
|
|
|
int width = gtk_widget_get_allocated_width(widget);
|
|
int height = gtk_widget_get_allocated_height(widget);
|
|
gtk_render_background(style_ctx, cr, 0, 0, width, height);
|
|
|
|
GtkBorder padding;
|
|
gtk_style_context_get_padding(style_ctx, GTK_STATE_FLAG_NORMAL, &padding);
|
|
PangoContext *pango = gtk_widget_get_pango_context(widget);
|
|
PangoLayout *layout = create_text_layout(head, pango, style_ctx);
|
|
|
|
gdk_cairo_set_source_rgba(cr, &fg);
|
|
cairo_move_to(cr, padding.left, padding.top);
|
|
pango_cairo_show_layout(cr, layout);
|
|
g_object_unref(layout);
|
|
return TRUE;
|
|
}
|
|
|
|
void wd_create_overlay(struct wd_output *output) {
|
|
output->overlay_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_decorated(GTK_WINDOW(output->overlay_window), FALSE);
|
|
gtk_window_set_resizable(GTK_WINDOW(output->overlay_window), FALSE);
|
|
gtk_widget_add_events(output->overlay_window, GDK_STRUCTURE_MASK);
|
|
|
|
g_signal_connect(output->overlay_window, "realize",
|
|
G_CALLBACK(window_realize), output);
|
|
g_signal_connect(output->overlay_window, "map",
|
|
G_CALLBACK(window_map), output);
|
|
g_signal_connect(output->overlay_window, "unmap",
|
|
G_CALLBACK(window_unmap), output);
|
|
g_signal_connect(output->overlay_window, "draw",
|
|
G_CALLBACK(window_draw), output);
|
|
|
|
GtkStyleContext *style_ctx = gtk_widget_get_style_context(
|
|
output->overlay_window);
|
|
gtk_style_context_add_class(style_ctx, "output-overlay");
|
|
gtk_widget_show(output->overlay_window);
|
|
}
|
|
|
|
void wd_destroy_overlay(struct wd_output *output) {
|
|
if (output->overlay_window != NULL) {
|
|
gtk_widget_destroy(output->overlay_window);
|
|
output->overlay_window = NULL;
|
|
}
|
|
}
|