|
|
|
@@ -33,6 +33,16 @@ typedef struct {
|
|
|
|
|
gboolean final_visible;
|
|
|
|
|
gboolean preview_visible;
|
|
|
|
|
gboolean x11;
|
|
|
|
|
gboolean layer_shell;
|
|
|
|
|
gboolean click_through;
|
|
|
|
|
gboolean dragging;
|
|
|
|
|
int bottom_margin;
|
|
|
|
|
int position_x;
|
|
|
|
|
int position_y;
|
|
|
|
|
double drag_start_px;
|
|
|
|
|
double drag_start_py;
|
|
|
|
|
int drag_origin_x;
|
|
|
|
|
int drag_origin_y;
|
|
|
|
|
} CaptioneerOverlay;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
@@ -41,6 +51,9 @@ typedef struct {
|
|
|
|
|
char *preview_text;
|
|
|
|
|
} CaptioneerUpdate;
|
|
|
|
|
|
|
|
|
|
// Implemented on the Go side via //export so drag placement can be persisted.
|
|
|
|
|
extern void captioneerOverlayPositionChanged(int x, int y);
|
|
|
|
|
|
|
|
|
|
static void captioneer_append_warning(GString *warnings, const char *message) {
|
|
|
|
|
if (warnings->len > 0) {
|
|
|
|
|
g_string_append(warnings, " ");
|
|
|
|
@@ -126,15 +139,198 @@ static GdkMonitor *captioneer_select_monitor(
|
|
|
|
|
return monitor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_set_input_passthrough(CaptioneerOverlay *overlay, GString *warnings) {
|
|
|
|
|
if (!gdk_display_supports_input_shapes(gtk_widget_get_display(GTK_WIDGET(overlay->window)))) {
|
|
|
|
|
captioneer_append_warning(warnings, "This display backend does not support pointer click-through.");
|
|
|
|
|
static GdkMonitor *captioneer_overlay_monitor(CaptioneerOverlay *overlay) {
|
|
|
|
|
if (overlay->monitor != NULL) {
|
|
|
|
|
return overlay->monitor;
|
|
|
|
|
}
|
|
|
|
|
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(overlay->window));
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(overlay->window));
|
|
|
|
|
if (surface != NULL) {
|
|
|
|
|
GdkMonitor *monitor = gdk_display_get_monitor_at_surface(display, surface);
|
|
|
|
|
if (monitor != NULL) {
|
|
|
|
|
return monitor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return captioneer_monitor_by_index(display, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_update_input_region(CaptioneerOverlay *overlay) {
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(overlay->window));
|
|
|
|
|
if (surface == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(overlay->window));
|
|
|
|
|
if (!overlay->click_through || overlay->dragging) {
|
|
|
|
|
gdk_surface_set_input_region(surface, NULL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Limit hit-testing to the caption bubble so the transparent full-width
|
|
|
|
|
// chrome stays click-through. CTRL+drag still works because clicks that
|
|
|
|
|
// land on the bubble include modifier state.
|
|
|
|
|
int bubble_width = gtk_widget_get_width(overlay->bubble);
|
|
|
|
|
int bubble_height = gtk_widget_get_height(overlay->bubble);
|
|
|
|
|
if (bubble_width <= 0 || bubble_height <= 0) {
|
|
|
|
|
cairo_region_t *empty = cairo_region_create();
|
|
|
|
|
gdk_surface_set_input_region(surface, empty);
|
|
|
|
|
cairo_region_destroy(empty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
double native_x = 0, native_y = 0;
|
|
|
|
|
if (!gtk_widget_translate_coordinates(overlay->bubble, GTK_WIDGET(overlay->window), 0, 0, &native_x, &native_y)) {
|
|
|
|
|
native_x = 0;
|
|
|
|
|
native_y = 0;
|
|
|
|
|
}
|
|
|
|
|
cairo_rectangle_int_t rect = {
|
|
|
|
|
.x = (int)native_x,
|
|
|
|
|
.y = (int)native_y,
|
|
|
|
|
.width = bubble_width,
|
|
|
|
|
.height = bubble_height,
|
|
|
|
|
};
|
|
|
|
|
cairo_region_t *region = cairo_region_create_rectangle(&rect);
|
|
|
|
|
gdk_surface_set_input_region(surface, region);
|
|
|
|
|
cairo_region_destroy(region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_apply_placement(CaptioneerOverlay *overlay) {
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(overlay->window));
|
|
|
|
|
if (surface == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
GdkMonitor *monitor = captioneer_overlay_monitor(overlay);
|
|
|
|
|
if (monitor == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
GdkRectangle geometry;
|
|
|
|
|
gdk_monitor_get_geometry(monitor, &geometry);
|
|
|
|
|
int width = gdk_surface_get_width(surface);
|
|
|
|
|
int height = gdk_surface_get_height(surface);
|
|
|
|
|
if (width <= 1 || height <= 1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int x = overlay->position_x;
|
|
|
|
|
int y = overlay->position_y;
|
|
|
|
|
if (x < 0) {
|
|
|
|
|
x = (geometry.width - width) / 2;
|
|
|
|
|
}
|
|
|
|
|
if (y < 0) {
|
|
|
|
|
y = geometry.height - height - overlay->bottom_margin;
|
|
|
|
|
}
|
|
|
|
|
if (x < 0) {
|
|
|
|
|
x = 0;
|
|
|
|
|
}
|
|
|
|
|
if (y < 0) {
|
|
|
|
|
y = 0;
|
|
|
|
|
}
|
|
|
|
|
if (x + width > geometry.width) {
|
|
|
|
|
x = MAX(0, geometry.width - width);
|
|
|
|
|
}
|
|
|
|
|
if (y + height > geometry.height) {
|
|
|
|
|
y = MAX(0, geometry.height - height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (overlay->layer_shell) {
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_LEFT, TRUE);
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_TOP, TRUE);
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_RIGHT, FALSE);
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_BOTTOM, FALSE);
|
|
|
|
|
gtk_layer_set_margin(overlay->window, GTK_LAYER_SHELL_EDGE_LEFT, x);
|
|
|
|
|
gtk_layer_set_margin(overlay->window, GTK_LAYER_SHELL_EDGE_TOP, y);
|
|
|
|
|
gtk_layer_set_margin(overlay->window, GTK_LAYER_SHELL_EDGE_RIGHT, 0);
|
|
|
|
|
gtk_layer_set_margin(overlay->window, GTK_LAYER_SHELL_EDGE_BOTTOM, 0);
|
|
|
|
|
} else if (overlay->x11) {
|
|
|
|
|
Display *display = GDK_SURFACE_XDISPLAY(surface);
|
|
|
|
|
Window xid = GDK_SURFACE_XID(surface);
|
|
|
|
|
int root_x = geometry.x + x;
|
|
|
|
|
int root_y = geometry.y + y;
|
|
|
|
|
XSizeHints *size_hints = XAllocSizeHints();
|
|
|
|
|
if (size_hints != NULL) {
|
|
|
|
|
size_hints->flags = USPosition | PPosition;
|
|
|
|
|
size_hints->x = root_x;
|
|
|
|
|
size_hints->y = root_y;
|
|
|
|
|
XSetWMNormalHints(display, xid, size_hints);
|
|
|
|
|
XFree(size_hints);
|
|
|
|
|
}
|
|
|
|
|
XMoveWindow(display, xid, root_x, root_y);
|
|
|
|
|
XFlush(display);
|
|
|
|
|
}
|
|
|
|
|
captioneer_update_input_region(overlay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean captioneer_reposition_x11(gpointer data) {
|
|
|
|
|
captioneer_apply_placement(data);
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_surface_size_changed(GObject *object, GParamSpec *spec, gpointer data) {
|
|
|
|
|
(void)object;
|
|
|
|
|
(void)spec;
|
|
|
|
|
g_idle_add(captioneer_reposition_x11, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean captioneer_ctrl_held(GdkEvent *event) {
|
|
|
|
|
return (gdk_event_get_modifier_state(event) & GDK_CONTROL_MASK) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_drag_begin(GtkGestureDrag *gesture, double x, double y, gpointer data) {
|
|
|
|
|
(void)x;
|
|
|
|
|
(void)y;
|
|
|
|
|
CaptioneerOverlay *overlay = data;
|
|
|
|
|
GdkEvent *event = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(gesture));
|
|
|
|
|
if (event == NULL || !captioneer_ctrl_held(event)) {
|
|
|
|
|
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_DENIED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(overlay->window));
|
|
|
|
|
if (surface == NULL) {
|
|
|
|
|
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_DENIED);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
overlay->dragging = TRUE;
|
|
|
|
|
captioneer_update_input_region(overlay);
|
|
|
|
|
gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
|
|
|
|
|
|
|
|
|
GdkMonitor *monitor = captioneer_overlay_monitor(overlay);
|
|
|
|
|
GdkRectangle geometry = {0};
|
|
|
|
|
if (monitor != NULL) {
|
|
|
|
|
gdk_monitor_get_geometry(monitor, &geometry);
|
|
|
|
|
}
|
|
|
|
|
int width = gdk_surface_get_width(surface);
|
|
|
|
|
int height = gdk_surface_get_height(surface);
|
|
|
|
|
int origin_x = overlay->position_x;
|
|
|
|
|
int origin_y = overlay->position_y;
|
|
|
|
|
if (origin_x < 0) {
|
|
|
|
|
origin_x = (geometry.width - width) / 2;
|
|
|
|
|
}
|
|
|
|
|
if (origin_y < 0) {
|
|
|
|
|
origin_y = geometry.height - height - overlay->bottom_margin;
|
|
|
|
|
}
|
|
|
|
|
overlay->drag_origin_x = origin_x;
|
|
|
|
|
overlay->drag_origin_y = origin_y;
|
|
|
|
|
gtk_gesture_drag_get_start_point(gesture, &overlay->drag_start_px, &overlay->drag_start_py);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_drag_update(GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer data) {
|
|
|
|
|
CaptioneerOverlay *overlay = data;
|
|
|
|
|
if (!overlay->dragging) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
(void)gesture;
|
|
|
|
|
overlay->position_x = overlay->drag_origin_x + (int)offset_x;
|
|
|
|
|
overlay->position_y = overlay->drag_origin_y + (int)offset_y;
|
|
|
|
|
captioneer_apply_placement(overlay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_drag_end(GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer data) {
|
|
|
|
|
(void)gesture;
|
|
|
|
|
CaptioneerOverlay *overlay = data;
|
|
|
|
|
if (!overlay->dragging) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
overlay->position_x = overlay->drag_origin_x + (int)offset_x;
|
|
|
|
|
overlay->position_y = overlay->drag_origin_y + (int)offset_y;
|
|
|
|
|
overlay->dragging = FALSE;
|
|
|
|
|
captioneer_apply_placement(overlay);
|
|
|
|
|
captioneerOverlayPositionChanged(overlay->position_x, overlay->position_y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_apply_x11_hints(CaptioneerOverlay *overlay) {
|
|
|
|
@@ -177,51 +373,11 @@ static void captioneer_apply_x11_hints(CaptioneerOverlay *overlay) {
|
|
|
|
|
XFlush(display);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean captioneer_reposition_x11(gpointer data) {
|
|
|
|
|
CaptioneerOverlay *overlay = data;
|
|
|
|
|
if (!overlay->x11 || overlay->monitor == NULL) {
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(overlay->window));
|
|
|
|
|
if (surface == NULL) {
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
GdkRectangle geometry;
|
|
|
|
|
gdk_monitor_get_geometry(overlay->monitor, &geometry);
|
|
|
|
|
int width = gdk_surface_get_width(surface);
|
|
|
|
|
int height = gdk_surface_get_height(surface);
|
|
|
|
|
if (width <= 1 || height <= 1) {
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
int bottom_margin = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(overlay->window), "bottom-margin"));
|
|
|
|
|
int x = geometry.x + (geometry.width - width) / 2;
|
|
|
|
|
int y = geometry.y + geometry.height - height - bottom_margin;
|
|
|
|
|
Display *display = GDK_SURFACE_XDISPLAY(surface);
|
|
|
|
|
Window xid = GDK_SURFACE_XID(surface);
|
|
|
|
|
XSizeHints *size_hints = XAllocSizeHints();
|
|
|
|
|
if (size_hints != NULL) {
|
|
|
|
|
size_hints->flags = USPosition | PPosition;
|
|
|
|
|
size_hints->x = x;
|
|
|
|
|
size_hints->y = y;
|
|
|
|
|
XSetWMNormalHints(display, xid, size_hints);
|
|
|
|
|
XFree(size_hints);
|
|
|
|
|
}
|
|
|
|
|
XMoveWindow(display, xid, x, y);
|
|
|
|
|
XFlush(display);
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_surface_size_changed(GObject *object, GParamSpec *spec, gpointer data) {
|
|
|
|
|
(void)object;
|
|
|
|
|
(void)spec;
|
|
|
|
|
g_idle_add(captioneer_reposition_x11, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void captioneer_update_window_visibility(CaptioneerOverlay *overlay) {
|
|
|
|
|
gboolean visible = overlay->final_visible || overlay->preview_visible;
|
|
|
|
|
gtk_widget_set_visible(overlay->bubble, visible);
|
|
|
|
|
gtk_widget_set_visible(GTK_WIDGET(overlay->window), visible);
|
|
|
|
|
if (visible && overlay->x11) {
|
|
|
|
|
if (visible) {
|
|
|
|
|
g_idle_add(captioneer_reposition_x11, overlay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -236,9 +392,7 @@ static gboolean captioneer_apply_update(gpointer data) {
|
|
|
|
|
|
|
|
|
|
overlay->preview_visible = update->preview_text[0] != '\0';
|
|
|
|
|
if (overlay->preview_visible) {
|
|
|
|
|
char *preview = g_strdup_printf("… %s", update->preview_text);
|
|
|
|
|
gtk_label_set_text(GTK_LABEL(overlay->preview_label), preview);
|
|
|
|
|
g_free(preview);
|
|
|
|
|
gtk_label_set_text(GTK_LABEL(overlay->preview_label), update->preview_text);
|
|
|
|
|
} else {
|
|
|
|
|
gtk_label_set_text(GTK_LABEL(overlay->preview_label), "");
|
|
|
|
|
}
|
|
|
|
@@ -287,6 +441,8 @@ static CaptioneerOverlay *captioneer_overlay_new(
|
|
|
|
|
int max_width,
|
|
|
|
|
const char *monitor_selection,
|
|
|
|
|
int click_through,
|
|
|
|
|
int position_x,
|
|
|
|
|
int position_y,
|
|
|
|
|
char **warning_message,
|
|
|
|
|
char **error_message
|
|
|
|
|
) {
|
|
|
|
@@ -315,6 +471,10 @@ static CaptioneerOverlay *captioneer_overlay_new(
|
|
|
|
|
CaptioneerOverlay *overlay = g_new0(CaptioneerOverlay, 1);
|
|
|
|
|
overlay->loop = g_main_loop_new(NULL, FALSE);
|
|
|
|
|
overlay->x11 = x11;
|
|
|
|
|
overlay->click_through = click_through != 0;
|
|
|
|
|
overlay->bottom_margin = bottom_margin;
|
|
|
|
|
overlay->position_x = position_x;
|
|
|
|
|
overlay->position_y = position_y;
|
|
|
|
|
|
|
|
|
|
char *monitor_error = NULL;
|
|
|
|
|
if (wayland && g_strcmp0(monitor_selection, "auto") == 0) {
|
|
|
|
@@ -336,20 +496,17 @@ static CaptioneerOverlay *captioneer_overlay_new(
|
|
|
|
|
gtk_window_set_resizable(overlay->window, FALSE);
|
|
|
|
|
gtk_widget_set_focusable(GTK_WIDGET(overlay->window), FALSE);
|
|
|
|
|
gtk_widget_add_css_class(GTK_WIDGET(overlay->window), "captioneer-overlay");
|
|
|
|
|
g_object_set_data(G_OBJECT(overlay->window), "bottom-margin", GINT_TO_POINTER(bottom_margin));
|
|
|
|
|
g_signal_connect(overlay->window, "close-request", G_CALLBACK(captioneer_window_close), overlay);
|
|
|
|
|
|
|
|
|
|
if (wayland && gtk_layer_is_supported()) {
|
|
|
|
|
overlay->layer_shell = TRUE;
|
|
|
|
|
gtk_layer_init_for_window(overlay->window);
|
|
|
|
|
gtk_layer_set_namespace(overlay->window, "captioneer");
|
|
|
|
|
gtk_layer_set_layer(overlay->window, GTK_LAYER_SHELL_LAYER_OVERLAY);
|
|
|
|
|
// Stretch across the bottom edge so the compositor pins the surface
|
|
|
|
|
// there (single-edge anchoring can leave it floating mid-screen on
|
|
|
|
|
// Plasma). The bubble itself stays content-width and centered.
|
|
|
|
|
// Pin with LEFT+TOP margins so placement is exact. Stretching LEFT+RIGHT
|
|
|
|
|
// made Plasma size the surface oddly and parked captions mid-screen.
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_LEFT, TRUE);
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_RIGHT, TRUE);
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_BOTTOM, TRUE);
|
|
|
|
|
gtk_layer_set_margin(overlay->window, GTK_LAYER_SHELL_EDGE_BOTTOM, bottom_margin);
|
|
|
|
|
gtk_layer_set_anchor(overlay->window, GTK_LAYER_SHELL_EDGE_TOP, TRUE);
|
|
|
|
|
gtk_layer_set_exclusive_zone(overlay->window, 0);
|
|
|
|
|
gtk_layer_set_keyboard_mode(overlay->window, GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
|
|
|
|
|
if (overlay->monitor != NULL) {
|
|
|
|
@@ -384,7 +541,7 @@ static CaptioneerOverlay *captioneer_overlay_new(
|
|
|
|
|
overlay->bubble = box;
|
|
|
|
|
gtk_widget_add_css_class(box, "captioneer-bubble");
|
|
|
|
|
gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_set_valign(box, GTK_ALIGN_END);
|
|
|
|
|
gtk_widget_set_valign(box, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_hexpand(box, FALSE);
|
|
|
|
|
gtk_widget_set_vexpand(box, FALSE);
|
|
|
|
|
gtk_widget_set_size_request(box, effective_width, -1);
|
|
|
|
@@ -417,6 +574,14 @@ static CaptioneerOverlay *captioneer_overlay_new(
|
|
|
|
|
gtk_widget_add_css_class(overlay->preview_label, "captioneer-preview");
|
|
|
|
|
gtk_window_set_child(overlay->window, box);
|
|
|
|
|
|
|
|
|
|
GtkGesture *drag = gtk_gesture_drag_new();
|
|
|
|
|
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(drag), GDK_BUTTON_PRIMARY);
|
|
|
|
|
gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(drag), GTK_PHASE_CAPTURE);
|
|
|
|
|
g_signal_connect(drag, "drag-begin", G_CALLBACK(captioneer_drag_begin), overlay);
|
|
|
|
|
g_signal_connect(drag, "drag-update", G_CALLBACK(captioneer_drag_update), overlay);
|
|
|
|
|
g_signal_connect(drag, "drag-end", G_CALLBACK(captioneer_drag_end), overlay);
|
|
|
|
|
gtk_widget_add_controller(overlay->bubble, GTK_EVENT_CONTROLLER(drag));
|
|
|
|
|
|
|
|
|
|
char *font_family_rule = captioneer_font_family_rule(font_family);
|
|
|
|
|
char *css = g_strdup_printf(
|
|
|
|
|
"window.captioneer-overlay { background-color: transparent; box-shadow: none; }"
|
|
|
|
@@ -433,15 +598,20 @@ static CaptioneerOverlay *captioneer_overlay_new(
|
|
|
|
|
|
|
|
|
|
gtk_widget_realize(GTK_WIDGET(overlay->window));
|
|
|
|
|
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(overlay->window));
|
|
|
|
|
if (click_through) {
|
|
|
|
|
captioneer_set_input_passthrough(overlay, warnings);
|
|
|
|
|
if (overlay->click_through) {
|
|
|
|
|
if (!gdk_display_supports_input_shapes(display)) {
|
|
|
|
|
captioneer_append_warning(warnings, "This display backend does not support pointer click-through.");
|
|
|
|
|
} else {
|
|
|
|
|
captioneer_update_input_region(overlay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (x11) {
|
|
|
|
|
captioneer_apply_x11_hints(overlay);
|
|
|
|
|
}
|
|
|
|
|
g_signal_connect(surface, "notify::width", G_CALLBACK(captioneer_surface_size_changed), overlay);
|
|
|
|
|
g_signal_connect(surface, "notify::height", G_CALLBACK(captioneer_surface_size_changed), overlay);
|
|
|
|
|
}
|
|
|
|
|
gtk_widget_set_visible(GTK_WIDGET(overlay->window), FALSE);
|
|
|
|
|
captioneer_apply_placement(overlay);
|
|
|
|
|
|
|
|
|
|
if (warnings->len > 0) {
|
|
|
|
|
*warning_message = g_string_free(warnings, FALSE);
|
|
|
|
@@ -516,6 +686,28 @@ type Sink struct {
|
|
|
|
|
closed bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
positionSaverMu sync.Mutex
|
|
|
|
|
positionSaver func(x, y int)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// SetPositionSaver registers a callback invoked when the user finishes a CTRL-drag.
|
|
|
|
|
func SetPositionSaver(save func(x, y int)) {
|
|
|
|
|
positionSaverMu.Lock()
|
|
|
|
|
positionSaver = save
|
|
|
|
|
positionSaverMu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//export captioneerOverlayPositionChanged
|
|
|
|
|
func captioneerOverlayPositionChanged(x, y C.int) {
|
|
|
|
|
positionSaverMu.Lock()
|
|
|
|
|
save := positionSaver
|
|
|
|
|
positionSaverMu.Unlock()
|
|
|
|
|
if save != nil {
|
|
|
|
|
save(int(x), int(y))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func New(settings config.OverlaySettings) (*Sink, string, error) {
|
|
|
|
|
backend := C.CString(string(settings.Backend))
|
|
|
|
|
fontFamily := C.CString(settings.FontFamily)
|
|
|
|
@@ -535,6 +727,8 @@ func New(settings config.OverlaySettings) (*Sink, string, error) {
|
|
|
|
|
C.int(settings.MaxWidth),
|
|
|
|
|
monitor,
|
|
|
|
|
boolInt(settings.ClickThrough),
|
|
|
|
|
C.int(settings.PositionX),
|
|
|
|
|
C.int(settings.PositionY),
|
|
|
|
|
&warningMessage,
|
|
|
|
|
&errorMessage,
|
|
|
|
|
)
|
|
|
|
|