LINUX.ORG.RU

Перемещение собственного окна программно в X11

 , ,


0

3

Здравствуйте

Имеется окно, у которого нет заголовка и бордера. Всё изображение внутри окна рисуется через OpenGL, включая собственный заголовок и кнопки типа развернуть/свернуть. И всё вроде бы более-менее работает, но есть большая проблема с тем, чтобы поставить окно в нужное место на экране. В первую очередь это необходимо, чтобы тащить окно мышью за собственный заголовок, но не тут-то было. Окно при перетаскивании дребезжит по всему экрану, хотя и перемещается в нужную сторону. Само перемещение делается сейчас так:

// Изменение положения окна на рабочем столе
void GPlatformUnixOGL::SetWindowRect(CMagicRect* rect)
{
    int wi=rect->Width();
    int he=rect->Height();

   XMoveResizeWindow(display, window, rect->left, rect->top, wi, he);

    XFlush(display);
}

Попытки делать что-то типа override_redirect=True; приводят к тому, что окно нельзя свернуть, хотя дребезга и становится меньше.

Что я делаю не так ? Мне всего-то и надо, чтобы окно заняло то положение, которое я ему отправляю.

Само окно создается так:

int glxAttribs[]={
GLX_RGBA,               // Используем режим RGBA
GLX_DOUBLEBUFFER,       // Двойная буферизация
None                    // Завершаем список атрибутов
};

// Выбираем визуальный формат
XVisualInfo* visualInfo=glXChooseVisual(display, screen, glxAttribs);

Window rootWindow=RootWindow(display, screen);
XSetWindowAttributes windowAttributes;
windowAttributes.colormap=XCreateColormap(display, rootWindow, visualInfo->visual, AllocNone);
windowAttributes.background_pixmap=None;  // ОТКЛЮЧАЕМ очистку фона!
windowAttributes.backing_store=Always;    // Просим X11 сохранять буфер окна

window=XCreateWindow(
display,
rootWindow,
wx, wy, view_width, view_height, // Позиция и размер окна
0,                               // Без границы
visualInfo->depth,               // Глубина цвета
InputOutput,                     // Тип окна
visualInfo->visual,              // Визуальный формат
CWColormap | CWEventMask,        // Устанавливаем атрибуты окна
&windowAttributes
);


// Указываем тип окна NORMAL
Atom wmWindowType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
Atom wmWindowNormal = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
XChangeProperty(display, window, wmWindowType, XA_ATOM, 32, PropModeReplace, (unsigned char *)&wmWindowNormal, 1);

// Отключаем рамки и заголовок
Atom motifHints = XInternAtom(display, "_MOTIF_WM_HINTS", False);
struct {
long flags;
long functions;
long decorations;
long input_mode;
long status;
} hints = { 2, 0, 0, 0, 0 };
XChangeProperty(display, window, motifHints, motifHints, 32, PropModeReplace, (unsigned char *)&hints, 5);

XSizeHints *h = XAllocSizeHints();
h->flags = PPosition | PSize | PResizeInc | PBaseSize;
h->x = 100;          // Координаты X
h->y = 100;          // Координаты Y
h->width = 640;      // Ширина окна
h->height = 480;     // Высота окна
h->base_width = 640; // Базовая ширина
h->base_height = 480; // Базовая высота

XSetWMNormalHints(display, window, h);
XFree(h);

Всё изображение внутри окна рисуется через OpenGL, включая собственный заголовок и кнопки типа развернуть/свернуть.

Тебя вяленый покусал, что-ли?

anonymous
()

Окно при перетаскивании дребезжит по всему экрану, хотя и перемещается в нужную сторону.

Подозреваю что у тебя проблема с расчётом координат.

rect->left, rect->top

Вот эти значения откуда там берутся? Надо в начале перемещения запомнать все нужные числа и потом смотреть только на положение мышки и пересчитывать по ней. Я когда делал перемещение/ресайз в своём WM, сначала пересчитывал всё при каждом движении мышки и тоже дребезжание получалось из-за разных факторов.

И уточни, что именно ты под «дребезжит» имеешь ввиду - меняются координаты начала окна или его размер?

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от Skullnet

Почему антипаттерн, может он хочет индивидуальный дизайн.

Но, автор, если ты хочешь эмулировать таким способом стандартный заголовок в обычном его оформлении - то, да, так делать не надо. Заголовки в разных WM разные, а в каких-то их вообще нет.

firkax ★★★★★
()

Что за wm? И как он обрабатывает configurenotify?. Думаю они друг другу мешают при перетаскивании окна мышью, поэтому у тебя окно туда-сюда елозит. Это асинхронно происходит. Выставь для окна wm hints, wm window type(dock или dialog). А wm дальше сам справится.

arrecck ★★★
()
Ответ на: комментарий от firkax

И уточни, что именно ты под «дребезжит» имеешь ввиду - меняются координаты начала окна или его размер?

Окно прыгало с правой части экрана на левую. Но, кстати, вы оказались правы - я нашел у себя проблему с координатами. Теперь прыгать перестало и даже вроде перемещается нормально, но есть ещё проблема.

Odin_KG
() автор топика
Ответ на: комментарий от arrecck

Что за wm?

echo $DESKTOP_SESSION
/usr/share/xsessions/plasma

echo $XDG_CURRENT_DESKTOP
KDE

У меня достаточно неновая Fedora

Выставь для окна wm hints, wm window type(dock или dialog).

Dock вообще лучше не ставить, потому что мой Linux такое окно всегда отображает поверх других окон, а кроме того оно не сворачивается.

Odin_KG
() автор топика
Ответ на: комментарий от Skullnet

Антипаттерн и юзай тогда SDL.

Хотелось бы обойтись без SDL, тем более, что там нет Drag & Drop.

Вообще сейчас ситуация такова, что по крайней мере на моем компьютере всё более-менее заработало. Единственное что всё портит - если приложение стартует с развернутым окном (точнее я его не разворачиваю, а просто указываю положение во весь экран), то потом приложение уже размер окна менять отказывается (просто игнорирует новые размер и положение). Тут надо ещё подумать…

Odin_KG
() автор топика
Ответ на: комментарий от firkax

Я могу нарисовать самостоятельно заголовок с кнопками развернуть, свернуть, закрыть и бордер ? Мне ещё потребуется, чтобы собственные размеры у этих элементов назначить.

Odin_KG
() автор топика
Ответ на: комментарий от MOPKOBKA

В SDL нет полноценного Drag & Drop - там есть очень ограниченный вариант, чтобы текст скопировать. Мне же нужно свой формат данных регистрировать.

Odin_KG
() автор топика
Ответ на: комментарий от Odin_KG

Зачем тебе собственные размеры для них? Смотри, пользователь настроил себе систему так как ему нравится, включая заголовки окон с кнопками (или их отсутствие, как у меня), а ты «нет, глупый юзер, твои кастомные настройки я пошлю нафиг и буду рисовать как мне хочется». Зачем?

firkax ★★★★★
()
Ответ на: комментарий от firkax

Вопрос, «зачем ?» он как бы неверный, потому что если бы мне не надо было, я бы так не делал. Мне нужно, чтобы работало как минимум на Windows, Mac и на Linux, причем уже понятно, что Linux будет всячески сопротивляться из-за того, что по сути там нормально работает только 1 вариант конкретно через WM (ну уж меньше этого реализовать просто невозможно), а всё остальное несмотря на «заявленность», как я понимаю, в общем случае не работает. Сколько сил на эту уходит, а в результате находится какая-то хрень, которая всё блокирует и хоть начинай с начала.

Odin_KG
() автор топика
Ответ на: комментарий от Odin_KG

Что значит неверный? Очень даже верный. Ты на него сам знаешь ответ?

Linux будет всячески сопротивляться из-за того, что по сути там нормально работает только 1 вариант

Нет, не так. Linux тут вообще ни при чём на самом деле, он окнами не занимается. И это не придирки к словам в данном случае, а существенное уточнение: окнами занимается графический сервер и приложения к нему, тебе надо думать именно о них. А уж запущен он на линуксе или ещё где-то, разницы почти не сделает.

Графических серверов сейчас есть два: один нормальный (xorg), впорой школоподелка, распиаренная зачем-то редхатом (вайланд). Так вот, на втором у тебя окно перемещать вообще не получится, потому что он это не поддерживает и наоборот заявляет что это такая защита от злонамеренных прог. Но не будем о грустном (рекомендую просто указать что вайланд не поддерживается и ссылку на инструкции по замене его на xorg).

Xorg, хоть сам по себе это и один проект, но управление окнами он делегирует оконному менеджеру. Разных оконных менеджеров - сотни, часть их них похожа друг на друга, часть не похожа. Так вот, часть их них использует оформление окон с заголовками, кнопками на них и самодельными рамками, за заголовки можно перемещать, кнопками ещё что-то делать. Другие ничего этого не рисуют, окно это просто прямоугольник это экране, целиком заполненный той картинкой, которую там сделало приложение. Управление при этом делается либо через хоткеи клавиатуры, либо через таскбар. Перемещение окон, хотя по умолчанию в Xorg разрешено, но оконный менеджер запросто может его заблокировать (как и ресайз), и разрешать делать только через интерфейс, нарисованный оконным менеджером. Всякие кнопки сворачивания и прочего - это вообще не дефолт никаким боком, это целиком «творение» оконного менеджера, и работают они так, как выбрал его автор. Послать команду «свернуть окно» мимо них не всегда можно.

а всё остальное несмотря на «заявленность»,

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

Всё равно, без указания цели всех этих действий сложно что-то посоветовать.

Думаю что вариант «где можно - работает, где нельзя - кнопки/перемещение/ресайз тупо ничего не делают» тебя устроит, потому что другого всё равно нет. Если юзер настроил себе систему так, что прога не может сама сворачивать своё окно - то ты ничего с этим не поделаешь, юзер сильнее. А если ты юзера начнёшь пытаться перехитрить, то это уже будет поведение, подобное всяким вирусам, и твою прогу просто снесут.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

Благодарю за подробные объяснения!

Я не знаю где ты нашёл заявленность.

Заявленность состоит в том, что есть функции которые устанавливают положение окна, а также, например, окно можно свернуть программно. Но… если ничего это не работает в общем случае, то таких функций вообще быть не должно - просто не надо дурачить людей, раз это всё в реальности неприменимо.

Odin_KG
() автор топика
Ответ на: комментарий от Odin_KG

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

Причём перехват не всегда чтобы заблокировать. Вот пример: допустим, у проги есть окно 100х100 пикселей, и она хочешь его поставить (левый верхний угол) на x=10, y=10. Но оконный менеджер вокруг этого окна рисует рамку и заголовок. Рамка, допустим, занимает 3 пикселя, заголовок 20 пикселей. И так вот, программа шлёт команду «переместить окно на 10,10», оконный менеджер её перехватывает и заменяет на:

1) переместить рамку на 10,10

2) переместить заголовок на 13,13

3) переместить основное окно (то самое) на 13,33

В итоге окно расположено немного не так, где хотела прога, потому что прога не учитывает что над окном есть ещё 23 пикселя служебным элементов. Аналогично и с ресайзом - прога хочет размер 100х100, а оконный менеджер должен думать как всё сделать чтобы ещё и заголовок с рамкой влезли - то ли уменьшить настоящий размер окна, то ли увеличить размер окна+заголовка+рамки.

Есть, конечно, и другой вариант - окно переместить на 10,10, как прога и хотела, а заголовок пририсовать выше (он на y=-10 окажется и уползёт за границу экрана).

Но и для блокировки перемещения этот перехват тоже, конечно, может использоваться.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

Благодарю за пояснения ещё раз!

У этих функций в документации указано, что их может перехватывать оконный менеджер и подменять их поведение.

Хм… может я что-то неправильно понимаю, но я бы такие функции просто повыбрасывал, чтобы не создавать путаницу.

И так вот, программа шлёт команду «переместить окно на 10,10», оконный менеджер её перехватывает и заменяет на:

Т.е. X11 воспринимает координаты окна как координаты клиентской части, а не самого окна ??? Подозреваю, что это же и размера касается.

Вы мне сказали главное, что делать на Linux-е так, как я хотел нельзя. Т.е. я вероятно должен выбросить свой заголовок и бордер, и использовать здесь возможности WM. Вроде бы понятно, что делать, но не знаю, сколько там в сумме мелких проблем будет, так как на такой вариант у меня приложение изначально не рассчитано.

Но я теперь вообще не понимаю, могу ли я хоть как-то самостоятельно указать положение окна (хотя бы размер) ? Просто у меня запоминается положение окна в момент закрытия и при следующем запуске окно должно появится в том же месте.

Далее, как я понимаю, я не могу никак убрать с заголовка кнопки типа «свернуть» и «закрыть». Хотя нажатие «закрыть» по-крайней мере можно отловить и сделать свою реакцию А все остальные кнопки на заголовке вообще получаются никак мною не управляемы ?

Odin_KG
() автор топика
Ответ на: комментарий от Odin_KG

Мне нужно, чтобы работало как минимум на Windows, Mac и на Linux

Очень странный подход к многоплаторменности. Надо стремится чтобы в любой ОС гуй программы выглядел максимально нативно. Для Линукса это означает оставить перемещение, размеры и прочие свойства окна, типа минимизировано/максимизировано или на каком десктопе и пр. исключительно на усмотрение юзера, а в программе обрабатывать только уведомления о новом размере выданной рабочей области и масштабировать интерфейс в зависимости от полученных значений, ничего более.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

Я с вами во многом согласен, но я всё это изначально делал на Windows, и мне, честно говоря, в голову не могло прийти, что в Linux-е запрещены, так сказать, «базовые операции» с окном. Но теперь что-то менять уже поздно - постараюсь как-нибудь выкрутиться.

Odin_KG
() автор топика
Ответ на: комментарий от Odin_KG

Хм… может я что-то неправильно понимаю, но я бы такие функции просто повыбрасывал, чтобы не создавать путаницу.

Никакой путаницы они не создают, и, более того, если их выбросить то перемещение окон сломается полностью. Надо только понимать, что программа может быть так или иначе ограничена в своих правах перемещать окно, исходя из желаний пользователя (в виде того что он установил соответствующее WM). Это не большая поломка, чем как если сказать «программа cat сломана, она не хочет выводить на экран файл /etc/shadow, давайте её лучше удалим».

$ cat /etc/shadow
cat: /etc/shadow: Отказано в доступе

Эти функции нужны именно для того, чтобы программа могла сообщить «хочу расположить своё окно там-то», и во многих случаях её даже буквально выполнят, но не всегда. WM стараются, чтобы их вмешательство ничью логику не ломало, но при желании конечно можно сделать так, чтоб ломаться об них.

Т.е. X11 воспринимает координаты окна как координаты клиентской части, а не самого окна ??? Подозреваю, что это же и размера касается.

Дело в том что у чистого Xorg окно это и есть клиентская часть, максимум он ещё умеет рисовать рамку (одноцветную). Всё остальное дорисовывают к нему wm, способы там есть разные и на координаты они влияют тоже по-разному. Посмотрел сейчас на каком-то xfce, там сделано так: wm создаёт своё окно в границах вместе с заголовком и рамкой, окно приложения переоформляет внутренним к нему, а ещё делает (тоже внутренними к этому окну) ещё пачку «окон» больше 10 штук - разные части рамки и заголовок. Если он пропустит запрос на перемещение как есть - то окно приложения уедет не пойми куда внутри родительского окна с рамками. Вместо этого WM перемещает родительское окно. Можешь с помощью xwininfo поизучать как всё устроено запускаешь её, кликаешь по окну, он показывает его координаты или ещё что-то. xwininfo -children покажет родительские и вложенные окна, xwininfo -frame позволит «выбирать» не только окна приложений, но и служебные окна WM (заголовок итд). Кстати, xwininfo как-то узнаёт какое из окон «клиентское» (если без -frame нажать на заголовок - он всё равно покажет инфу про клиентское окно которое под ним), можешь посмотреть как он это делает.

Но я теперь вообще не понимаю, могу ли я хоть как-то самостоятельно указать положение окна (хотя бы размер)

Можно, конечно. Функции то есть, и они работают. Не надо бояться WM, у него нет цели всё сломать, он в большинстве случаев только адаптирует твой запрос к своей структуре окон. Ну а если заблокировано - значит пользователь так хочет и не надо спорить.

Просто у меня запоминается положение окна в момент закрытия и при следующем запуске окно должно появится в том же месте.

Оставь как есть, если работает - всё норм. В каких-то wm может начать ползти вверх или вниз на высоту заголовка при каждом запуске. Можно делать так: позиционируй окно функцией, потом запрашивай координаты, которые получились. Надо добиться чтобы запрошенные координаты совпали с теми которые были перед закрытием тоже запрошены.

Далее, как я понимаю, я не могу никак убрать с заголовка кнопки типа «свернуть» и «закрыть». Хотя нажатие «закрыть» по-крайней мере можно отловить и сделать свою реакцию А все остальные кнопки на заголовке вообще получаются никак мною не управляемы ?

Зависит от WM. Можешь посмотреть тут https://www.tronche.com/gui/x/icccm/sec-4.html - большинство WM так или иначе поддерживают эту спецификацию, может там можно отключить. И ещё тут https://specifications.freedesktop.org/wm-spec/latest/ - ей тоже многие (но не все) WM следуют, может быть там можно убрать эти кнопки.

Вообще я не очень понимаю зачем нужно блокировать сворачивание окна, а потом самостоятельно опять рисовать эту кнопку.

firkax ★★★★★
()

если получить и учесть extents (сколько добавлено к границам окна) и главное - выставить hints user_position=true, то есть что расположением окошка рулишь сам,

то дёргаться ничего не будет и WM не должен препятствовать. Но это для X и вменяемых wm. Не про вялого или «сбацал тайлинг на расте»

MKuznetsov ★★★★★
()
Ответ на: комментарий от Odin_KG

А в винде-то зачем программе самой своё окно таскать? Там тот же самый подход прекрасно работает, на самом деле. GetClientRect() - это всё что требуется программе знать. Просто в венде только один WM, только одно DE и только один десктоп и всё укушенное.

Stanson ★★★★★
()