lv2 / suil

Library for loading and wrapping LV2 plugin UIs
ISC License
11 stars 5 forks source link

x11_in_gtk: add support for min_size and base_size in XSizeHints of … #5

Closed brummer10 closed 5 years ago

brummer10 commented 5 years ago

…plugin window. Thus allow init a Plugin UI with a default size and shrink it afterwards to the min size.

This is more comparable with the x11_in_qt wrappers.

x42 commented 5 years ago

Thus allow init a Plugin UI with a default size and shrink it afterwards to the min size

XSetNormalHints() and XResizeWindow() on the plugin's X11 window are honored, except the latter is overridden by a host when it restores the plugin's UI (e.g. Ardour saves the size and position)

x42 commented 5 years ago

PS. it seems guitarix calls lv2_resize early on during instantiation. Thereby forcing the min. window size to the current size. After removing that call the GUI should be resizable without this. Did you try this?

brummer10 commented 5 years ago

It seems that we ain't talk about the same issue. What I like to archive is the ability to open a X11 plugin UI with the base size, not with the min size, but still allow a user to shrink the UI to the min size, if required. Currently any call from the plugin UI to the resize function in the suil wrapper set the newly given size as new min size, and ignore therewith the settings for min_width/min_height in the XSetNormalHints() of the plugin UI. This patch simply reset the gtk widget property's to the values given in XSetNormalHints(). So the UI has the ability to request a size, without forcing that to be the min size.

And yes, surely I've tried to disable the resize call during init, but, as I said, that wouldn't lead to open the UI with the base size at all.

x42 commented 5 years ago

As discussed on IRC, prefer using size-request and remove the gtk_set_size_request(). Something along the lines of

diff --git a/src/x11_in_gtk2.c b/src/x11_in_gtk2.c
index 7ec9592..29dcd5e 100644
--- a/src/x11_in_gtk2.c
+++ b/src/x11_in_gtk2.c
@@ -37,6 +37,9 @@ struct _SuilX11Wrapper {
  const LV2UI_Idle_Interface* idle_iface;
  guint                       idle_id;
  guint                       idle_ms;
+ bool                        custom_size;
+ int                         req_width;
+ int                         req_height;
 };

 struct _SuilX11WrapperClass {
@@ -249,6 +252,28 @@ suil_x11_wrapper_key_event(GtkWidget*   widget,
 }

 static void
+suil_x11_on_size_request(GtkWidget*     widget,
+                         GtkRequisition* requisition)
+{
+ SuilX11Wrapper* const self = SUIL_X11_WRAPPER(widget);
+ if (self->custom_size) {
+   requisition->width  = self->req_width;
+   requisition->height = self->req_height;
+ } else if (x_window_is_valid(self)) {
+   GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(self->plug));
+   XSizeHints hints;
+   memset(&hints, 0, sizeof(hints));
+   XGetNormalHints(GDK_WINDOW_XDISPLAY(window),
+       (Window)self->instance->ui_widget,
+       &hints);
+   if (hints.flags & PMinSize) {
+     requisition->width  = hints.min_width;
+     requisition->height = hints.min_height;
+   }
+ }
+}
+
+static void
 suil_x11_on_size_allocate(GtkWidget*     widget,
                           GtkAllocation* a)
 {
@@ -283,12 +308,19 @@ suil_x11_wrapper_init(SuilX11Wrapper* self)
  self->instance   = NULL;
  self->idle_iface = NULL;
  self->idle_ms    = 1000 / 30;  // 30 Hz default
+ self->req_width  = 0;
+ self->req_height = 0;
+ self->custom_size = false;
 }

 static int
 wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
 {
- gtk_widget_set_size_request(GTK_WIDGET(handle), width, height);
+ SuilX11Wrapper* const wrap = SUIL_X11_WRAPPER(handle);
+ wrap->req_width   = width;
+ wrap->req_height  = height;
+ wrap->custom_size = true;
+ gtk_widget_queue_resize(GTK_WIDGET(handle));
  return 0;
 }

@@ -329,6 +361,11 @@ wrapper_wrap(SuilWrapper*  wrapper,
                   NULL);

  g_signal_connect(G_OBJECT(wrap),
+                  "size-request",
+                  G_CALLBACK(suil_x11_on_size_request),
+                  NULL);
+
+ g_signal_connect(G_OBJECT(wrap),
                   "size-allocate",
                   G_CALLBACK(suil_x11_on_size_allocate),
                   NULL);
brummer10 commented 5 years ago

Your patch didn't add the expected functionality. Still can't shrink the window below the initial size, still no support for aspect ratio.

brummer10 commented 5 years ago

As pointed out in IRC, to allow shrinking the UI from a initial base size to a min size set by the plugin UI, we need to reset the size request of the widget to the min size AFTER size-allocate. Thus will allow a user to shrink the widget down to min size.

brummer10 commented 5 years ago
else if (x_window_is_valid(self)) {
+   GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(self->plug));
+   XSizeHints hints;
+   memset(&hints, 0, sizeof(hints));
+   XGetNormalHints(GDK_WINDOW_XDISPLAY(window),
+       (Window)self->instance->ui_widget,
+       &hints);
+   if (hints.flags & PMinSize) {
+     requisition->width  = hints.min_width;
+     requisition->height = hints.min_height;
+   }

this part is useless, it will never ever run. The size-request callback is only fired when the widget itself (or a child) do a size-request, not when a user resize the widget. And then, always the var self->custom_size is set to true. When the user change the widget size, only size-allocate is fired.

x42 commented 5 years ago

still no support for aspect ratio.

That is correct. Since the child window is inside a host-provided container. There can only be a letterbox.

In case of Ardour, the host adds a preset-bar at the top, setting a fixed minimum width, jalv adds a menu-bar.. Other hosts may add a left-side column (eg. Reaper once it gains LV2 support). The size of which is unknown. The best that can be done is center the child window and add letterbox bars either left/right or top/bottom.

x42 commented 5 years ago

Putting your comments together I think your plugin calls ui_resize() and thus enforces a minimum size.

Can we try for a clean solution:

In this case self->custom_size is always false (!!!).

The same works on other OS, one can set size and min.size independently, while LV2's ui_resize() enforces the size.

PS. We could specify

wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
{
  if (width == 0 && height == 0) {
    /* update minimum size only */
    gtk_widget_queue_resize(GTK_WIDGET(handle));
    return;
  }
 ...
}
brummer10 commented 5 years ago

Still that ain't work. In that case the UI will open with the min size, regardless how I set the size with XResizeWindow. And I wonder, what you believe were the size for the GTK window should come from, as in that case, the size request is explicitly set to min_size for the GTK Window.
And even more bad the behave is then on a Qt based host.

brummer10 commented 5 years ago

okay, there is a simple solution to solve my issue within your patch, without introduce additional size-request calls. What did you think about this:


static void
suil_x11_on_size_request(GtkWidget*     widget,
                            GtkRequisition* requisition)
{
    SuilX11Wrapper* const self = SUIL_X11_WRAPPER(widget);
    static int o =1;
    if (self->custom_size && o) {
        requisition->width  = self->req_width;
        requisition->height = self->req_height;
    } else if (x_window_is_valid(self)) {
        GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(self->plug));
        XSizeHints hints;
        memset(&hints, 0, sizeof(hints));
        XGetNormalHints(GDK_WINDOW_XDISPLAY(window),
            (Window)self->instance->ui_widget,
            &hints);
        if (hints.flags & PMinSize) {
            requisition->width  = hints.min_width;
            requisition->height = hints.min_height;
        } else {
            requisition->width  = self->req_width;
            requisition->height = self->req_height;
        }
    }
    o ^= 1;
}

it use the simple fact that the size-request signal is fired 2 times, so the window open with the given size (if given) and reset the size_request to the min_size (if given) in the same run.

brummer10 commented 5 years ago

and therewith we could as well add support for base_size:


static void
suil_x11_on_size_request(GtkWidget*     widget,
                         GtkRequisition* requisition)
{
    SuilX11Wrapper* const self = SUIL_X11_WRAPPER(widget);
    static int o = 1;
    if (self->custom_size && o) {
        requisition->width  = self->req_width;
        requisition->height = self->req_height;
    } else if (x_window_is_valid(self)) {
        GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(self->plug));
        XSizeHints hints;
        memset(&hints, 0, sizeof(hints));
        long supplied;
        XGetWMNormalHints(GDK_WINDOW_XDISPLAY(window),
            (Window)self->instance->ui_widget,
            &hints, &supplied);
        if (o && supplied & PBaseSize) {
            requisition->width  = hints.base_width;
            requisition->height = hints.base_height;
        } else if (supplied & PMinSize) {
            requisition->width  = hints.min_width;
            requisition->height = hints.min_height;
        } else {
            requisition->width  = self->req_width;
            requisition->height = self->req_height;
        }
    }
    o ^= 1;
}

so if a plugin don't call ui_resize but set XSizeHints, it will open with the base size (if given) or with the min_size, if no base_size is given. To archive that we need to check in wrapper_resize if a size is given to set custom_size accordingly:

    if (width > 0 && height > 0) {
        wrap->custom_size = true;
    }
drobilla commented 5 years ago

Merged as db07a21, thanks.