Как запрограммировать окно (с подсказкой о соотношении сторон) для правильного изменения размера на рабочем столе Gnome? - PullRequest
0 голосов
/ 10 мая 2019

Окно простой программы Xlib с подсказкой соотношения сторон не изменяется должным образом с Gnome.Когда изменение размера приводит к тому, что верхняя часть окна становится выше верхней части рабочего стола Gnome (или выше нижней части панели панели Gnome Classic в верхней части рабочего стола), в окне отображается странное поведение.Когда это происходит, окно выполняет одно из двух действий - переключается на минимальную ширину или исчезает.

Проблемы в основном возникают при изменении размера с помощью левого или правого маркеров изменения размера.Есть ли способ обойти это в коде?Является ли это ошибкой в ​​Gnome (я использую Gnome 3.28.2 в CentOS 7.6)?

Я пробовал следующие обходные пути, когда это происходит:

  1. Программно изменяя размеры окна изатем игнорируем следующее событие ConfigureNotify.
  2. Установка подсказки о минимальном размере окна.Это предотвращает сокращение окна до ширины, равной единице, но по-прежнему имеет проблемы.
  3. Установка минимальных и максимальных размеров окна одинаковыми значениями, поэтому размер окна не может быть изменен.Затем отмените максимальный размер при перемещении окна, чтобы можно было снова изменить его размер.
  4. Установка положения y окна, чтобы оно не выходило за рабочий стол.
  5. Комбинации вышеперечисленного.

В примере кода показана одна попытка решения с комбинацией 3 и 4.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>  // XSetWMNormalHints

char *g_title = "Hello World X Example + Gnome workaround";

float aspect_ratio = 0.75;

void
configure_aspect (Display *display, Window window, float ratio)
{
    XSizeHints hints;
    unsigned char *prop = NULL;

    hints.min_width    = 100;
    hints.min_height   = 100 * ratio;
    hints.min_aspect.x = 1000;
    hints.min_aspect.y = 1000 * ratio;
    hints.max_aspect.x = 1000;
    hints.max_aspect.y = 1000 * ratio;
    hints.flags = PMinSize | PAspect;

    XSetWMNormalHints(display, window, &hints);
}

void
configure_noresize(Display *display, Window window, int w, int h)
{
    XSizeHints hints;

    hints.min_width    = w;
    hints.min_height   = h;
    hints.max_width    = w;
    hints.max_height   = h;
    hints.flags = PMinSize | PMaxSize;
    printf("***************set fixed size***************\n");

    XSetWMNormalHints(display, window, &hints);
}

configure_resize(Display *display, Window window, float ratio)
{
    XSizeHints hints;

    hints.min_width    = 100;
    hints.min_height   = 100 * ratio;
    hints.max_width    = INT_MAX;
    hints.max_height   = INT_MAX;
    hints.min_aspect.x = 1000;
    hints.min_aspect.y = 1000 * ratio;
    hints.max_aspect.x = 1000;
    hints.max_aspect.y = 1000 * ratio;
    hints.flags = PMinSize | PMaxSize | PAspect;
    printf("***************enable resize***************\n");

    XSetWMNormalHints(display, window, &hints);
}

void
Redraw(Display *display, Window window, GC gc, XFontStruct *fontinfo, int w, int h)
{
    static char     hello_string[] = "Hello World";
    static int      hello_string_length = sizeof(hello_string) - 1;
    XWindowAttributes   window_attributes;
    int         text_x;
    int         text_y;
    int         width, height;
    int         font_direction, font_ascent, font_descent;
    XCharStruct     text_structure;

    // get font info and calculate position of text
    XTextExtents(fontinfo, hello_string, hello_string_length,
        &font_direction, &font_ascent, &font_descent,
        &text_structure);
    XGetWindowAttributes(display, window, &window_attributes);
    text_x = (window_attributes.width - text_structure.width)/2;
    text_y = (window_attributes.height -
            (text_structure.ascent+text_structure.descent))/2;

    // draw the text
    XDrawString(display, window, gc,
        text_x, text_y, hello_string, hello_string_length);
}

void
configure(Display *display, Window window, XConfigureEvent *e)
{
    static int last_x = 0;
    static int last_y = 0;
    static int last_w = 0;
    static int last_h = 0;
    static int is_size_fixed = 0;
    int x;
    int y;
    int is_resize = 0;

    if (0 == last_w) {
        // initialize last width and height values
        last_w = e->width;
        last_h = e->height;
    }

    if ((e->width != last_w) || (e->height != last_h)) {
        // this is a resize event
        is_resize = 1;
    }

    if (is_resize) {
        // DEVNOTE: coordinates are client (drawing area) coordinates
        // for resize
        Window dummy;

        printf("Configure(resize): location(%d, %d), size(%d, %d)\n",
               e->x, e->y, e->width, e->height);

        XTranslateCoordinates(display, window, DefaultRootWindow(display),
                      e->x, e->y, &x, &y, &dummy);
        x -= e->x + 5;
        y -= e->y + 5;
        printf("configure: xlt_location(%d, %d)\n", x, y);
        if (y < 61) {
            // happens when resizing from left or right side
            int new_h = last_h + (last_y - 60);
            int new_w = new_h / aspect_ratio;
            printf("noresize (w2, h2)=(%d, %d)\n", new_w, new_h);

            // this sets the size to what the size would have been
            // if the top of the window hadn't passed the panel bar
            configure_noresize(display, window, new_w, new_h);

            is_size_fixed = 1;
            XStoreName(display, window, "Move window to re-enable resizing");
        } else {
            last_x = x;
            last_y = y;
        }
    } else {
        // DEVNOTE; coordinates are screen coordinates for move
        printf("Configure(move): location(%d, %d), size(%d, %d)\n",
               e->x, e->y, e->width, e->height);
        if (is_size_fixed) {
            if (e->y > 65) {
                configure_resize(display, window, aspect_ratio);
                XStoreName(display, window, g_title);
                is_size_fixed = 0;
            }
        }

        last_x = x;
        last_y = y;
    }

    last_w = e->width;
    last_h = e->height;

    printf("\n");
}

void
main_event_loop(Display *display, Window window, GC gc, XFontStruct *fontinfo)
{
    XEvent          event;
    int         do_loop = 1;

    do {
        XNextEvent(display, &event);

        switch (event.type) {
        case Expose:
        {
            XExposeEvent *e = (XExposeEvent *)&event;
            int w = e->width;
            int h = e->height;
            Redraw(display, window, gc, fontinfo, w, h);
            break;
        }
        case ClientMessage:
            // window closing, break out of event loop to quit
            do_loop = 0;
            break;
        case ConfigureNotify:
            printf("ConfigureNotify event\n");
            configure(display, window, (XConfigureEvent *)&event);
            break;
        default:
            break;
        }
    } while (do_loop);
}

int
main (int argc, char *argv[])
{
    Display         *display;
    Visual          *visual;
    int         depth;
    XSetWindowAttributes    win_attributes;
    Window          window;
    XFontStruct     *fontinfo;
    XGCValues       gc_values;
    GC          graphics_context;
    long            event_mask;

    display = XOpenDisplay(NULL);
    visual = DefaultVisual(display, 0);
    depth  = DefaultDepth(display, 0);

    win_attributes.background_pixel = XWhitePixel(display, 0);
    window = XCreateWindow(display, XRootWindow(display, 0),
                    0, 0, 400, 300, 5, depth,
                    InputOutput, visual,
                    CWBackPixel,
                    &win_attributes);
    XStoreName(display, window, g_title);

    // setup event mask
    event_mask = ExposureMask | StructureNotifyMask;
    XSelectInput(display, window, event_mask);

    // Tell Window Manager we are interested in window close event
    Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, window, &WM_DELETE_WINDOW, 1);

    fontinfo = XLoadQueryFont(display, "10x20");

    gc_values.font = fontinfo->fid;
    gc_values.foreground = XBlackPixel(display, 0);
    graphics_context = XCreateGC(display, window,
                    GCFont+GCForeground, &gc_values);

    XMapWindow(display, window);
    configure_aspect(display, window, aspect_ratio);

    main_event_loop(display, window, graphics_context, fontinfo);

    XDestroyWindow(display, window);
    XCloseDisplay(display);

    return 0;
}

В идеале верхняя часть окна должна останавливаться в верхней части рабочего стола и изменять размервниз и в стороны (сохраняя аспект).Приемлемо любое другое решение, которое не имеет условия, при котором окно исчезает, игнорирует аспект или проявляет другое плохое поведение.

...