Как мы уже упоминали ранее, свойство есть набор данных, ассоциированных с окном. Они хранятся в специальных таблицах в памяти компьютера, на котором работает сервер. Каждое свойство имеет имя. Разные окна могут иметь свойства с одинаковыми именами.
Поскольку передача имен - строк произвольной длины - от клиента к серверу может увеличить нагрузку на сеть, X идентифицирует свойства с помощью целых чисел - атомов. Процедура XInternAtom()
включает свойство с указанным именем в таблицу сервера и возвращает соответствующий атом. Полный список реализуемых X протоколом атомов можно найти в файле /usr/include/X11/Xatom.h
.
Данные свойства рассматриваются сервером как массив единиц длиной 8, 16 или 32 бита. Их конкретная интерпретация осуществляется программами-клиентами.
Каждое свойство имеет тип, который, в свою очередь, также задается тем или иным свойством. Например, свойство, соответствующее атому XA_STRING
, задает тип "строка".
Для работы со свойствами кроме XInternAtom()
используются следующие процедуры: XChangeProperty()
- меняет данные свойства: XGetWindowProperty()
- позволяет получить данные свойства.
Особую роль играют свойства, данные которых содержат строки текста. Они так и называются текстовыми и имеют тип "TEXT"
. Таковыми являются, например, имена (заголовки) окно, имена пиктограмм и т.д. Данные текстового свойства описываются структурой TXTextProperty
. Процедура XStringListToTextProperty()
переводит список строк в набор данных типа TXTextProperty
:
(* Эта переменная будет хранить созданное свойство. *) var window_title_property : TXTextProperty ; rc : TStatus; (* Строка, преобразуемая в свойство. *) const window_title : PChar = 'hello, world'; (* перевод строки в свойство X. *) rc := XStringListToTextProperty(@window_title, 1, @window_title_property); (* проверка успешности преобразования. *) if (rc = 0) then begin writeln( 'XStringListToTextProperty - нет памяти'); halt(1); end;
XTextPropertyToString()
выполняет обратное преобразование.
Менеджер окон - это специальный клиент, в задачи которого входит интерактивное перемещение окон по экрану, изменение их размеров, минимизация (превращение в пиктограмму) и многое другое. Чтобы облегчить менеджеру его нелегкую жизнь, программам рекомендуется при инициализации сообщить о себе определенную информацию. Передается она через предопределенные свойства, которые известны менеджеру и могут быть им прочитаны. Некоторые из свойств (так называемые стандартные) задавать обязательно. Все остальное определяется по усмотрению программы. Наиболее простой способ задать стандартные свойства - обратиться к процедурам XSetStandardProperties()
или XSetWMProperties()
.
Ниже перечисляются свойства, создаваемые для менеджера окон программами, а также процедуры для работы с ними.
Имя (заголовок) окна. Идентифицируется атомом XA_WM_NAME
и имеет тип "TEXT"
. Данные свойства - структура TXTextProperty
. Для задания свойства используется процедура XStoreName()
(XSetWMName()
). Получить его можно с помощью XFetchName()
(XGetWMName()
).
Имя пиктограммы. Идентифицируется атомом XA_WM_ICONNAME
и имеет тип "TEXT"
. Данные свойства - структура TXTextProperty
. Для задания свойства используется процедура XSetIconName()
(XSetWMIconName()
). Получить его можно с помощью XGetIconName()
(XGetWMIconName()
).
Рекомендации (hints) о геометрии окна. Идентифицируется атомом XA_WM_NORMAL_HINTS
и имеет тип XA_WM_SIZE_HINTS
. Данные свойства - структура типа TXSizeHints
. Для задания свойства используется процедура XSetNormalHints()
.
В ряде случаев стоит сообщить оконному менеджеру о том, какой размер окна мы хотим получить, и в каких пределах будут изменяться его размеры. Например, для терминальной программы (такой, как xterm), хотелось бы, чтобы окно всегда содержало полное количество строк и столбцов. В других случаях нежелательно давать возможность менять размер окна (например, в диалоговых окнах). Эти пожелания можно передать оконному менеджеру, хотя ничто не помешает ему их проигнорировать. Для этого необходимо создать структуру данных, заполнить ее необходимыми данными и затем использовать функцию XSetWMNormalHints()
:
(* указатель на структуру рекомендаций о размерах. *) var win_size_hints : PXSizeHints; win_size_hints := XAllocSizeHints(); if (win_size_hints=nil) then begin writeln('XAllocSizeHints - нет памяти'); halt(1); end; (* Инициализация структуры *) (* Вначале укажем, что передаются пожелания о размерах: *) (* установим минимальный и начальный размеры. *) win_size_hints^.flags := PSize OR PMinSize; (* Затем указываем требуемые границы. *) (* в нашем случае - создаем окно минимальным размером 300x200 *) (* пикселей и устанавливаем начальный размер в 400x250. *) win_size_hints^.min_width := 300; win_size_hints^.min_height := 200; win_size_hints^.base_width := 400; win_size_hints^.base_height := 250; (* Передаем пожелания о размерах оконному менеджеру. *) XSetWMNormalHints(display, win, win_size_hints); (* В конце необходимо освободить память из-под структуры. *) XFree(win_size_hints);
Дополнительные параметры окна: способ работы с клавиатурой, вид и положение пиктограммы. Идентифицируется атомом XA_WM_HINTS
и имеет тип XA_WM_HINTS
. Данные свойства - структура типа TXWMHints
. Для задания свойства используется процедура XSetWMHints()
. Структура типа XWMHints
, передаваемая функции XSetWMHints()
, должна быть подготовлена с помощью XAllocWMHints()
:
var win_hints : PXWMHints; icon_pixmap : TPixmap; const icon_bitmap_width=20; icon_bitmap_height=20; (* Определим битовое изображение в формате Х - *) (* оно может быть создано программой xpaint *) icon_bitmap_bits : array [0..59] of byte = ( $60, $00, $01, $b0, $00, $07, $0c, $03, $00, $04, $04, $00, $c2, $18, $00, $03, $30, $00, $01, $60, $00, $f1, $df, $00, $c1, $f0, $01, $82, $01, $00, $02, $03, $00, $02, $0c, $00, $02, $38, $00, $04, $60, $00, $04, $e0, $00, $04, $38, $00, $84, $06, $00, $14, $14, $00, $0c, $34, $00, $00, $00, $00 ); win_hints := XAllocWMHints(); if (win_hints=nil) then begin writeln('XAllocWMHints - нет памяти'); halt(1); end; (* установим пожелания о состоянии окна, позиции его иконки *) (* и ее виде *) win_hints^.flags := StateHint OR IconPositionHint OR IconPixmapHint; (* Загрузим заданное битовое изображение *) (* и создадим из него пиксельную карту Х. *) icon_pixmap := XCreateBitmapFromData(display, win, PChar(icon_bitmap_bits), icon_bitmap_width, icon_bitmap_height); if (icon_pixmap=nil) then begin writeln('XCreateBitmapFromData: ошибка создания пиксмапа'); halt(1); end; (* Затем детализируем желаемые изменения. *) (* в нашем случае - сворачиваем окно, определяем его иконку *) (* и устанавливаем позицию иконки в левом верхнем углу экрана. *) win_hints^.initial_state := IconicState; win_hints^.icon_pixmap := icon_pixmap; win_hints^.icon_x := 0; win_hints^.icon_y := 0; (* Передаем пожелания оконному менеджеру. *) XSetWMHints(display, win, win_hints); (* В конце необходимо освободить память из-под структуры. *) XFree(win_hints);
Получить данные свойства можно с помощью XGetWMHints()
.
Атрибут, характеризующий "временное" окно. Идентифицируется атомом XA_WM_TRANSIENT_FOR
и имеет тип XA_STRING
. Свойство задается для окон, появляющихся на экране для выполнения вспомогательных функций (диалоги, меню). Такие объекты рассматриваются менеджером по особому. Например, он может не добавлять к окну заголовок и рамку. Данные свойства - идентификатор окна родительского по отношению к данному. Задается свойство с помощью процедуры XSetTransientForHint()
.
Имена программы и ее класса, идентифицируется атомом XA_WM_CLASS
и имеет тип XA_STRING
. Данные свойства - структура типа TXClassHints
. Задается свойство с помощью процедуры XSetClassHint()
и может быть получено с помощью XGetClassHint()
.
Если окно (окна) программы имеют собственную цветовую палитру, то приложение должно соответствующим образом задать для него атрибут colormap
. Программа заносит идентификатор окна (идентификаторы окон) в список, ассоциированный со свойством, имя которого WM_COLORMAP_WINDOWS
. Делается это процедурой XSetWMColormapWindows()
. Получить список, уже находящийся в свойстве, можно, обратившись к XGetWMColormapWindows()
.
Когда окно открыто, пользователь посредством менеджера совершает над ним разные действия. Программе может быть желательно перехватывать некоторые из них. Так, например, если окно представляет собой редактор текста, и пользователь пытается его закрыть, то разумно спросить у сидящего за компьютером человека, а не желает ли он предварительно сохранить результаты редакции. Начиная с X11R4 системой предусматривается свойство с именем WM_PROTOCOLS
. Оно содержит список атомов, и каждый из них идентифицирует свойство, связанное с действиями, о которых надо оповещать программу. Эти свойства следующие:
WM_TAKE_FOCUS
- задается, если программа хочет передавать фокус ввода между своими окнами самостоятельно; в этом случае менеджер не будет управлять фокусом, ввода, а пошлет приложению событие ClientMessage
, у которого поле message_type
равно атому, соответствующему свойству WM_PROTOCOLS
, а поле data.l[0]
равно атому, соответствующему свойству WM_TAKE_FOCUS
; в ответ на это событие программа должна сама обратиться к XSetInputFocus()
для задания окна, имеющего фокус ввода;WM_SAVE_YOURSELF
&mdash задается, если программа хочет перехватить момент своего завершения; менеджер окон посылает приложению событие ClientMessage
, у которого поле message_type
равно атому, соответствующему свойству WM_PROTOCOLS
, а поле data.l[0]
равно атому, соответствующему свойству WM_SAVE_YOURSELF
; в ответ программа может сохранить свое текущее состояние;WM_DELETE_WINDOW
- задается, если программа хочет перехватить моменты, когда менеджер окон закрывает принадлежащие ей окна; менеджер окон посылает приложению событие ClientMessage
, у которого поле message_type
равно атому, соответствующему свойству WM_PROTOCOLS
, а поле data.l[0]
равно атому, соответствующему свойству WM_DELETE_WINDOW
; далее программа сама решает, оставить окно на экране или удалить его с помощью XDestroyWindow()
.Свойство WM_PROTOCOLS
задается процедурой XSetWMProtocols()
и может быть получено с помощью XGetWMProtocols()
.
Приведем фрагмент программы, задающей свойство WM_PROTOCOLS
и производящей соответствующую обработку событий.
. . . . . . . var prDisplay : PDisplay; nScreenNum : integer; prGC : TGC; rEvent : TXEvent; nWnd : TWindow; pnProtocol : array [0..1] of TAtom; nWMProtocols : TAtom; (* *Устанавливаем связь с сервером, получаем номер экрана, *создаем окно, выбираем события, обрабатываемые программой *) . . . . . . . (* Задаем свойство WM_PROTOCOLS *) pnProtocol [0] := XInternAtom (prDisplay, 'WM_TAKE_FOCUS', True); pnProtocol [1] := XInternAtom (prDisplay, 'WM_SAVE_YOURSELF', True); nWMProtocols := XInternAtom (prDisplay, 'WM_PROTOCOLS', True); XSetWMProtocols (prDisplay, nWnd, pnProtocol, 2); (* Показываем окно *) XMapWindow (prDisplay, nWnd); (* Цикл получения и обработки событий *) while true do begin XNextEvent (prDisplay, @rEvent); case (rEvent.type) of . . . . . . ClientMessage : begin if (rEvent.xclient.message_type = nWMProtocols) then begin if (rEvent.xclient.data.l[0] = pnProtocol[0]) then writeln('Receiving the input focus.') else if (rEvent.xclient.data.l[0] = pnProtocol[1]) then begin XCloseDisplay (prDisplay); halt(0); end; end; end; . . . . . . . end; end; . . . . . . .
Заказывается реакция на два события: получение фокуса ввода (WM_TAKE_FOCUS
) и завершение программы (WM_SAVE_YOURSELF
). Когда сервер посылает событие первого типа, задача печатает соответствующее сообщение на устройства вывода. При приходе события второго типа, программа закрывает связь с сервером и завершается.
1. Составьте программу, которая при получении фокуса ввода перекрашивает свое окно в другой цвет.
2. Составьте программу, порождающую два расположенных рядом дочерних окна, в которых отображаются графики функций sin(x) на отрезке [0; 2π] и exp(x) на отрезке [-2; 2]. Графики масштабировать по размеру окон.
3. Создайте окно, изменяющее свои размеры таким образом, чтобы мышь всегда была в его центре.
4. Создайте окно, "убегающее" от указателя мыши.
5. Создайте программу, которая по нажатию клавиши мыши в основном окне создает новое окно (не более 100 одновременно), а по нажатию клавиши мыши в дочернем окне удаляет его. Если дочернее окно существует более одной минуты, оно должно самоудаляться.
6. Создайте программу моделирования эволюции клеточного автомата "Жизнь", ячейки которого имею два состояния: пусто и заполнено. Если рядом с пустой ячейкой три заполненных, она заполняется. Если рядом с заполненной ячейкой меньше двух или больше трех заполненных, ячейка становится пустой. Размеры модельного поля - 64х64 ячейки, вначале поле пустое. По нажатию любой кнопки мыши состояние ячейки меняется на противоположное, по нажатию пробела осуществляется один шаг эволюции, а по нажатии Escape - выход из программы.