[ Назад ] [ Содержание ] [ Далее ]

Манипуляции с окнами

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

     Большую часть работы с окнами верхнего уровня берет на себя оконный менеджер. Для этих окон он создаёт своё обрамление и отрабатывает запросы пользователя на перемещение окон, изменение их размеров и других операций. Это намного облегчает работу программ, поскольку большинству программ хватает механизма управления окнами, предоставляемого оконным менеджером и им не требуется отслеживать команды с клавиатуры или мыши для управления своими окнами верхнего уровня. Им достаточно отрабатывать события, которые генерируются сервером после манипуляций, произведённых оконным менеджером для синхронизации содержимого окон и внутреннего состояния программы.

     Дочерние окна окон верхнего уровня оконными менеджерами не обрабатываются. Вообщем-то это редко и требуется. Самой большой проблемой, которая стоит в системе X Window из-за отсутствия возможности установки контроля оконным менеджером над дочерними окнами - это сложность реализации MDI (multi-document interface). Идея этого интерфейса заключается в том, что для дочерних окон существует такой-же механизм управления, как и для окон верхнего уровня. Этот вид интерфейса очень популярен в системе Microsoft Windows. В X Window как правило каждый открытый программой документ помещается в отдельное окно верхнего уровня.

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

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

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

     Для устранения отсутствия синхронизации между данными программы и фактическим положением окна, при перемещении окна генерируется событие ConfigureNotify, в котором указывается новая позиция и данные о размерах окна. Поэтому приложение, для которого важно знать координаты окна на экране, будет получать эти сведения.

    В нижеприведенном примере программа создает три окна, затем перемещает их, меняет порядок расположения. Затем плавно изменяет размер среднего окна и создает в нем несколько дочерних окон, располагая их по кругу. Затем дочерние окна устраивают хоровод в родительском окне и потом все окна закрываются.

    В этом же примере мы можем увидеть, что оконный менеджер не отслеживает параметры, устанавливаемые функцией XSetWMNormalHints для функций изменения геомерии, вызываемых из программы. Оконный менеджер позволит установить геометрию окна, выходящую за пределы указанных ограничений. Это означает, что эти ограничения существуют для пользователя, но не для программы.

Список некоторых функций, с помощью которых можно изменять внешний вид и расположение окон: Пример ex003.c:
#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <math.h>

#define pi 3.1415
#define childNum 12   /* Количество дочерних окон в хороводе */

/* Эта функция по точке в центре, радиусу и углу */
/* вычисляет точку на периметре круга            */
void calcXY(int cx, int cy, int rd, int ang, int *wx, int *wy)
 {
  *wx=cx+(int) (cos((float)ang*pi/180.0)*(float)rd);
  *wy=cy-(int) (sin((float)ang*pi/180.0)*(float)rd);
 };

int main(int argc, char **argv)
 {
  Display *ourDisplay;
  int ourScreen;
  Window rootWindow;
  Window  myWindow1, myWindow2, myWindow3; /* Рабочие окна верхнего уровня */
  Window childWindows[childNum];           /* Список дочерних окон         */
  int myDepth;
  XSetWindowAttributes myAttr;
  Visual *myVisual;  
  XSizeHints mySizeHints;
  XClassHint myClassHint;
  int i,j,wx,wy;
  
  ourDisplay=XOpenDisplay(NULL);
  if (ourDisplay==NULL)
    {
      printf("Не удалось установить соединение с графическим терминалом.\n");
      return 1;
    };

  /* Получим предварительные сведения */
  ourScreen=DefaultScreen(ourDisplay);         /* Экран по-умолчанию        */
  rootWindow=RootWindow(ourDisplay,ourScreen); /* Корневое окно             */
  myDepth=DefaultDepth(ourDisplay,ourScreen);  /* Глубина цветности экрана  */
  myVisual=DefaultVisual(ourDisplay,ourScreen);/* Визуальные характеристики */

  /* Выбираем фон для окон верхнего уровня */
  myAttr.background_pixel=WhitePixel(ourDisplay, ourScreen);

  /* Создаем три окна */
  myWindow1=XCreateWindow(ourDisplay,rootWindow,100, 100, 320, 200,
    0, myDepth, InputOutput, myVisual, CWBackPixel, &myAttr);
  myWindow2=XCreateWindow(ourDisplay,rootWindow,100, 100, 320, 200,
    0, myDepth, InputOutput, myVisual, CWBackPixel, &myAttr);
  myWindow3=XCreateWindow(ourDisplay,rootWindow,100, 100, 320, 200,
    0, myDepth, InputOutput, myVisual, CWBackPixel, &myAttr);
    
  /* Устанавливаем заголовки окон */
  XStoreName(ourDisplay,myWindow1,"Example window 1");
  XStoreName(ourDisplay,myWindow2,"Example window 2");
  XStoreName(ourDisplay,myWindow3,"Example window 3");

  /* Устанавливаем заголовки иконок */
  XSetIconName(ourDisplay,myWindow1,"example1");
  XSetIconName(ourDisplay,myWindow2,"example2");
  XSetIconName(ourDisplay,myWindow3,"example3");

  /* Устанавливаем ограничения на размеры окон */
  mySizeHints.flags=PMinSize | PMaxSize | PResizeInc;
  mySizeHints.min_width=192; mySizeHints.min_height=128;
  mySizeHints.max_width=640; mySizeHints.max_height=480;
  mySizeHints.width_inc=10; mySizeHints.height_inc=10;
  XSetWMNormalHints(ourDisplay, myWindow1, &mySizeHints);
  XSetWMNormalHints(ourDisplay, myWindow2, &mySizeHints);
  XSetWMNormalHints(ourDisplay, myWindow3, &mySizeHints);

  /* Делаем окна видимыми */
  XMapWindow(ourDisplay, myWindow1);
  XMapWindow(ourDisplay, myWindow2);
  XMapWindow(ourDisplay, myWindow3);

  XFlush(ourDisplay);
  sleep(3);

  /* Окна могли появиться в других координатах                */
  /* Передвигаем окна так, чтобы они наложились друг на друга */
  XMoveWindow(ourDisplay, myWindow1,0,100);
  XMoveWindow(ourDisplay, myWindow2,100,50);
  XMoveWindow(ourDisplay, myWindow3,50,0);

  XFlush(ourDisplay);
  sleep(3);
  
  /* Медленно перестраиваем окна следующим образом  */
  /* первое окно - поверх всех остальных            */
  /* второе окно - посредине                        */
  /* третье окно - в самом низу                     */
  XRaiseWindow(ourDisplay, myWindow2);
  XFlush(ourDisplay);
  sleep(1);
  XRaiseWindow(ourDisplay, myWindow1);
  XFlush(ourDisplay);
  sleep(3);

  /* Плавно увеличим размер второго окна    */
  for (i=1; i<200; i++)
   {
    XResizeWindow(ourDisplay,myWindow2,320+i,200+i);
    XFlush(ourDisplay);
    usleep(40);
   };

  /* Создадим второму окну набор дочерних окон, расположенных по кругу */
  for (i=0; i<childNum; i++)
   {

    /* Вычисляем координаты дочернего окна */
    calcXY(260,200,180,i*(360/childNum),&wx,&wy);

    /* Фон окон будет чёрным */
    myAttr.background_pixel=BlackPixel(ourDisplay, ourScreen);

    /* Дочерние окна появляются именно в указанных координатах */
    childWindows[i]=XCreateWindow(ourDisplay,myWindow2,wx-20,wy-10,40, 20,
                 0, myDepth, InputOutput, myVisual, CWBackPixel, &myAttr);
   };

  /* Включаем дочерние окна */
  XMapSubwindows(ourDisplay,myWindow2);

  XFlush(ourDisplay);
  sleep(1);
  
  /* Устраиваем хоровод из дочерних окон на 3 круга */
  for (j=1080; j>0; )
   {
    for (i=0; i<childNum; i++)
     {
      calcXY(260,200,180,i*(360/childNum)+j,&wx,&wy);
      XMoveWindow(ourDisplay,childWindows[i],wx-20,wy-10);
     };
    XFlush(ourDisplay);
    usleep(20);
    j-=2;
   };

  sleep(5);

  /* Уничтожаем окна */
  XDestroyWindow(ourDisplay, myWindow1);
  XFlush(ourDisplay);
  sleep(1);
  XDestroyWindow(ourDisplay, myWindow3);
  XFlush(ourDisplay);
  sleep(1);
  XDestroyWindow(ourDisplay, myWindow2);

  /* Закрываем соединение с сервером */
  XCloseDisplay(ourDisplay);

  return 0;
 };

[ Назад ] [ Содержание ] [ Далее ]