X11 в C++ — обрезка происходит всякий раз, когда устанавливается ResizeRedirectMask при изменении размера окна.

Сказка

Я сделал простую программу для рисования, отображающую графику, используя X11/Xlib для системы Linux.

В нем есть меню и редактор для создания рисунков.

Ему нужно знать размер окна, поэтому сначала я использовал XGetWindowAttributes()
, но на мой вкус он оказался слишком медленным.

Поскольку я выполнял функцию каждый кадр, моя программа работала медленно.

Затем я пытался запускать функцию реже, примерно каждые 4 кадра, но это вызывало мерцание .

Я хотел, чтобы событие могло генерироваться каждый раз при изменении размера окна, чтобы запускать функцию только при изменении размера.

Исходное исправление:

Я нашел ResizeRequest, который делает то, что я хочу и выводит размер окна в качестве бонуса.
Так что XGetWindowAttributes() мне больше не понадобилось. Это значительно ускорило работу программы.
Хорошо работает при уменьшении размера окна.

Новый выпуск:

Я обнаружил, что когда размер окна больше больше исходного размера, каждый объект, нарисованный за пределами исходного диапазона, обрезается< /strong> как показано на изображении.

Обрезанное изображение

Мы видим серые рамки, которые должны быть вокруг окна, но обрезаны все черные. Это особенно заметно для цветовой палитры вверху справа, которая состоит из 16 цветов, но 4 цвета обрезаны наполовину, а 8 не видны здесь вообще. .


Что я хотел бы сделать:

Я хотел бы знать, как решить проблему обрезки, возможно, решив один из следующих вопросов:

  1. Если я должен одобрить изменение размера при отправке события, как мне это сделать?
  2. Если я должен возиться с буферами, как я могу это сделать?

Треки:

В моей программе есть меню, в котором по-прежнему используется XGetWindowAttributes(), а редактор (показан на изображении) использует событие ResizeRequest, которое работает быстрее, чем XGetWindowAttributes().
Как и ожидалось , обрезка происходит в редакторе, а не в меню.
Но я заметил, что я могу изменить размер окна без обрезки, если я изменю его в меню, а затем в редакторе, и < strong>это работает
!

Затем я обнаружил, что обрезка происходит всякий раз, когда в функции XSelectInput() установлено значение ResizeRedirectMask.

Как указано в Руководстве по программированию Xlib в версии 10.11. 4 Страница событий ResizeRequest.

Любые попытки изменить размер другими клиентами затем перенаправляются.

Я думаю, это означает, что это не повлияет на размер окна (что верно, поскольку я тестировал XGetWindowAttributes() параллельно). Итак, я должен одобрить изменение размера, но как?

Как указано в ответе размер окна X11 заикается.

Я бы очистил передний буфер и дождался завершения изменения размера.

Как это сделать?


Образец кода:

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

Существует утверждение #if, которое вы можете изменить, чтобы увидеть разницу между ожидаемым (false) и реальным (true)

#include <X11/Xlib.h>
#include <cstdint>
#include <unistd.h>

#if true //Make it true to set ResizeRedirectMask and false to disable.
    #define Masks KeyPressMask|ResizeRedirectMask
#else
    #define Masks KeyPressMask
#endif

int main(){
    uint32_t RDM=0;

    uint16_t i=0;

    bool RunLoop=true;

    Display* XDisplay=XOpenDisplay(0);//Create a display
    Window XWindow=XCreateSimpleWindow(XDisplay,DefaultRootWindow(XDisplay),0,0,480,360,0,0,0);//Create a Window
    XMapWindow(XDisplay,XWindow);//Make the Window visible
    GC XGraphicCTX=XCreateGC(XDisplay,XWindow,0,0);//Create a Graphics Context

    //v Wait for a MapNotify XEvent for next commands
    XSelectInput(XDisplay,XWindow,StructureNotifyMask);
    while(1){
        XEvent e;
        XNextEvent(XDisplay,&e);
        if(e.type==MapNotify)break;
    }
    XSelectInput(XDisplay,XWindow,Masks); //Here is part of the magic error
    while(RunLoop){
        while(XPending(XDisplay)){//Get key changes
            XEvent Event;
            XNextEvent(XDisplay,&Event);
            if(Event.type==KeyPress){
                RunLoop=false;
            }
        }

        for(i=0;i<4096;i++){
            RDM=(RDM+1841)*9245;    //Not perfect but good enough
            XSetForeground(XDisplay,XGraphicCTX,RDM&0xFFFFFF);
            
            RDM=(RDM+1841)*9245;    //Not perfect but good enough
            XFillRectangle(XDisplay,XWindow,XGraphicCTX,RDM&0xFFFF,(RDM>>16),64,64);
        }

        XFlush(XDisplay);

        usleep(16667); //AHHH there is 666!
    }

    return 0;
}

Результат при изменении размера выглядит следующим образом: Неожиданный результат новой программы

Как мы видим, обрезка происходит всякий раз, когда установлено ResizeRedirectMask, вам даже не нужно реагировать на событие, чтобы это произошло!
Переключение оператора #if на false дает желаемое поведение, так как это отключит ResizeRedirectMask.
Эта конкретная программа не заботится о размере окна, большинству моих программ нужно знать размер, иначе они не работают.


Некоторые детали:

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

Событие ConfigureNotify кажется отлично работает, но оно мигает гораздо чаще, чем при использовании XGetWindowAttributes().
Почему оно мерцает?
Оно мерцает, потому что ConfigureNotify отправлено после завершения изменения размера, это означает, что оно обычно отправляет один кадр после изменения размера.
Это связано с Изменение размера окна X11 тормозит.
По крайней мере, это не тратит впустую много выступлений.
Я ищу способ исправить мерцание, как это может быть проще.

Обратите внимание, что вопрос полностью изменился, потому что проблема отличается от первоначальной. Но все еще в связи с исходной проблемой.


person Léolol DB    schedule 02.03.2020    source источник
comment
Во-первых, я бы попытался избежать Xlib и вместо этого использовать XCB, но это, вероятно, слишком далеко. Затем, я думаю, вы можете получать фактические уведомления при изменении размера, таким образом, вам не нужно повторно опрашивать значение, которое никогда не менялось.   -  person Ulrich Eckhardt    schedule 02.03.2020
comment
Пожалуйста, покажите свой код, если вам нужна помощь. Спасибо.   -  person Mark Setchell    schedule 11.03.2020
comment
Другой трюк здесь будет заключаться в том, чтобы опрашивать что-то такое дорогое время от времени, а не каждый тик. Или установите определенный размер, а затем реагируйте на события изменения размера (что я считаю гораздо более предпочтительным для различных языков и ситуаций).   -  person Mark Storer    schedule 16.03.2020
comment
Пожалуйста, задавайте по одному вопросу на каждый вопрос. Не меняйте вопрос полностью, потому что вы нашли решение, и теперь у вас есть новая проблема. Если вы нашли ответ на исходный вопрос, опубликуйте его. Тогда задайте новый вопрос.   -  person n. 1.8e9-where's-my-share m.    schedule 07.08.2020
comment
Код прост, см. заголовок. Обрезка происходит всякий раз, когда устанавливается ResizeRedirectMask. Думаю хватит!   -  person Léolol DB    schedule 07.08.2020
comment
Извините, я должен перерабатывать вопросы также из-за законов переполнения стека.   -  person Léolol DB    schedule 07.08.2020


Ответы (1)


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

Если вы не выберете StructureNotifyMask, ваше окно никогда не получит фактического изменения размера и не сможет ответить на них.

Когда вы получаете событие ConfigureNotify, которое произойдет, только если вы выберете StructureNotifyMask, оно будет содержать новую актуальную геометрию, поэтому ваше окно сможет обновляться.

person n. 1.8e9-where's-my-share m.    schedule 08.08.2020
comment
Это нормально, что так мерцает? Я пробовал это, и это работает, но мерцает больше, чем при использовании старого метода (XGetWindowAttributes()). Я предполагаю, что у него есть один кадр задержки между фактическим изменением размера и событием. Это то же самое, что и: / - person Léolol DB; 09.08.2020
comment
Я не совсем понимаю, что вы имеете ввиду. Ваша программа-пример вообще не мерцает. Изменение размера делает буфер недействительным, поэтому при каждом изменении размера рисунок начинается с чистого холста. Если вы хотите сохранить старое изображение, вам нужно сохранить старое изображение где-нибудь и перерисовать его при изменении размера. - person n. 1.8e9-where's-my-share m.; 09.08.2020
comment
Весь буфер становится недействительным просто потому, что по умолчанию старый буфер забыт. Чтобы сохранить старое содержимое, установите для атрибута окна bit_gravity значение, отличное от ForgetGravity. Это, конечно, не сохранит закадровые части. - person n. 1.8e9-where's-my-share m.; 09.08.2020
comment
Я пробовал все (bit_gravity и backing store), и он все еще немного мерцает. Я даже переписал инициализацию первой программы, которую я показал, и изменил поведение при смене окна, и оно по-прежнему теряет содержимое или мерцает. Думаю, этого достаточно для этого вопроса, и я сделаю новый, посвященный этой проблеме. - person Léolol DB; 10.08.2020
comment
В конце концов, я нашел решение. Это была простая проблема. Мне нужно было поместить маску значения CWBitGravity в функцию XCreateWindow() (которую я теперь использую вместо WCreateSimpleWindow()) - person Léolol DB; 10.08.2020