Обновить wm/x11/src/main.c
This commit is contained in:
parent
2b92a105e0
commit
96b2a979c9
@ -1,348 +1,470 @@
|
||||
// main.c - чистая версия для FreeBSD
|
||||
#ifndef __linux__
|
||||
#define __linux__ 1 // для совместимости
|
||||
#endif
|
||||
#ifndef _LINUX
|
||||
#define _LINUX 1
|
||||
#endif
|
||||
|
||||
#ifndef _SAPI
|
||||
#define _SAPI SciterAPI
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// X11, OpenGL и FreeBSD специфичные заголовки
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#include <GL/glx.h>
|
||||
|
||||
#include <uchar.h>
|
||||
|
||||
// Sciter C API (не C++!)
|
||||
#include <sciter-x.h>
|
||||
|
||||
// Глобальные переменные X11/GLX
|
||||
Display *dpy = NULL;
|
||||
Window root_win = 0;
|
||||
Window comp_win = 0;
|
||||
int screen = 0;
|
||||
int screen_width = 0;
|
||||
int screen_height = 0;
|
||||
|
||||
GLXContext glx_ctx = NULL;
|
||||
GLXFBConfig fb_config = 0;
|
||||
|
||||
Atom wm_delete_window = 0;
|
||||
|
||||
// Окно Sciter (структура вместо класса)
|
||||
HWINDOW g_sciter_hwnd = NULL;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Инициализация X11
|
||||
// -------------------------------------------------------------
|
||||
void init_x11(void) {
|
||||
dpy = XOpenDisplay(NULL);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "Ошибка: не могу открыть дисплей\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
screen = DefaultScreen(dpy);
|
||||
root_win = DefaultRootWindow(dpy);
|
||||
|
||||
screen_width = WidthOfScreen(DefaultScreenOfDisplay(dpy));
|
||||
screen_height = HeightOfScreen(DefaultScreenOfDisplay(dpy));
|
||||
|
||||
XSetWindowAttributes attrs;
|
||||
attrs.override_redirect = True;
|
||||
attrs.background_pixel = 0;
|
||||
attrs.border_pixel = 0;
|
||||
attrs.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | KeyReleaseMask;
|
||||
|
||||
comp_win = XCreateWindow(dpy, root_win,
|
||||
0, 0, screen_width, screen_height, 0,
|
||||
CopyFromParent, InputOutput, CopyFromParent,
|
||||
CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask,
|
||||
&attrs);
|
||||
|
||||
// Установка состояния окна (поверх всех и полноэкранный режим)
|
||||
Atom net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
|
||||
Atom net_wm_state_above = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False);
|
||||
Atom net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
|
||||
|
||||
Atom states[] = { net_wm_state_above, net_wm_state_fullscreen };
|
||||
XChangeProperty(dpy, comp_win, net_wm_state, XA_ATOM, 32,
|
||||
PropModeReplace, (unsigned char *)states, 2);
|
||||
|
||||
// Обработка закрытия окна
|
||||
wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(dpy, comp_win, &wm_delete_window, 1);
|
||||
|
||||
XMapWindow(dpy, comp_win);
|
||||
XRaiseWindow(dpy, comp_win);
|
||||
XFlush(dpy);
|
||||
|
||||
printf("Окно создано: 0x%lx (%dx%d)\n", comp_win, screen_width, screen_height);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Инициализация GLX для FreeBSD
|
||||
// -------------------------------------------------------------
|
||||
void init_glx(void) {
|
||||
XWindowAttributes win_attrs;
|
||||
XGetWindowAttributes(dpy, comp_win, &win_attrs);
|
||||
Visual *window_visual = win_attrs.visual;
|
||||
VisualID window_visual_id = XVisualIDFromVisual(window_visual);
|
||||
|
||||
// Атрибуты для GLX
|
||||
int fb_attribs[] = {
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RED_SIZE, 8,
|
||||
GLX_GREEN_SIZE, 8,
|
||||
GLX_BLUE_SIZE, 8,
|
||||
GLX_ALPHA_SIZE, 8,
|
||||
GLX_DEPTH_SIZE, 24,
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
None
|
||||
};
|
||||
|
||||
int num_fb;
|
||||
GLXFBConfig *fb_configs = glXChooseFBConfig(dpy, screen, fb_attribs, &num_fb);
|
||||
if (!fb_configs) {
|
||||
fprintf(stderr, "Ошибка: не удалось получить FBConfig\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Поиск совместимого FBConfig
|
||||
GLXFBConfig compatible_config = 0;
|
||||
for (int i = 0; i < num_fb; i++) {
|
||||
int visual_id = 0;
|
||||
glXGetFBConfigAttrib(dpy, fb_configs[i], GLX_VISUAL_ID, &visual_id);
|
||||
if ((VisualID)visual_id == window_visual_id) {
|
||||
compatible_config = fb_configs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!compatible_config) {
|
||||
compatible_config = fb_configs[0];
|
||||
printf("Предупреждение: используется несовместимый FBConfig\n");
|
||||
}
|
||||
|
||||
fb_config = compatible_config;
|
||||
XFree(fb_configs);
|
||||
|
||||
// Создание контекста OpenGL
|
||||
glx_ctx = glXCreateNewContext(dpy, fb_config, GLX_RGBA_TYPE, NULL, True);
|
||||
if (!glx_ctx) {
|
||||
fprintf(stderr, "Ошибка: не удалось создать GLX контекст\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Делаем контекст текущим
|
||||
if (!glXMakeCurrent(dpy, comp_win, glx_ctx)) {
|
||||
fprintf(stderr, "Ошибка: glXMakeCurrent не удался\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("GLX инициализирован. OpenGL: %s\n", glGetString(GL_VERSION));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Функции обратного вызова Sciter
|
||||
// -------------------------------------------------------------
|
||||
// Вызывается при необходимости перерисовки окна
|
||||
void sciter_on_paint(HWINDOW hwnd, UINT_PTR reserved) {
|
||||
// Sciter сам обрабатывает отрисовку через OpenGL
|
||||
// Просто вызываем обновление
|
||||
SciterUpdateWindow(hwnd);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Инициализация Sciter
|
||||
// -------------------------------------------------------------
|
||||
void init_sciter(void) {
|
||||
// Устанавливаем графический слой OpenGL (через Skia)
|
||||
SciterSetOption(NULL, SCITER_SET_GFX_LAYER, GFX_LAYER_SKIA_OPENGL);
|
||||
|
||||
// Включаем возможности для скриптов
|
||||
SciterSetOption(NULL, SCITER_SET_SCRIPT_RUNTIME_FEATURES,
|
||||
ALLOW_FILE_IO | ALLOW_SOCKET_IO | ALLOW_EVAL | ALLOW_SYSINFO);
|
||||
|
||||
// Создаем окно Sciter (без WinAPI, через X11)
|
||||
RECT frame_rect = {0, 0, screen_width, screen_height};
|
||||
|
||||
g_sciter_hwnd = SciterCreateWindow(
|
||||
SW_MAIN | SW_ENABLE_DEBUG,
|
||||
&frame_rect,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (!g_sciter_hwnd) {
|
||||
fprintf(stderr, "Ошибка: не удалось создать Sciter окно\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Устанавливаем обработчик отрисовки
|
||||
//sciter_callback(g_sciter_hwnd, SCITER_CB_PAINT, (void*)sciter_on_paint);
|
||||
|
||||
// HTML контент
|
||||
const char *html =
|
||||
"<!DOCTYPE html>"
|
||||
"<html><head><meta charset='UTF-8'><style>"
|
||||
"html { background: transparent; overflow: hidden; }"
|
||||
"body { background: rgba(26,26,26,0.9); color:#e0e0e0; "
|
||||
"display:flex; justify-content:center; align-items:center; "
|
||||
"height:100vh; margin:0; font-family:system-ui; }"
|
||||
".panel { background:#2b2b2b; border-radius:16px; padding:32px; "
|
||||
"text-align:center; box-shadow:0 8px 20px rgba(0,0,0,0.3); }"
|
||||
"h1 { color:#fff; margin-bottom:16px; }"
|
||||
"button { background:#0060df; color:#fff; border:none; border-radius:6px; "
|
||||
"padding:12px 24px; margin:8px; cursor:pointer; font-size:16px; "
|
||||
"transition:background 0.2s; }"
|
||||
"button:hover { background:#003eaa; }"
|
||||
"#status { margin-top:20px; color:#00ff9d; font-weight:bold; }"
|
||||
"</style></head>"
|
||||
"<body><div class='panel'><h1>Calista + Sciter</h1>"
|
||||
"<p>Sciter работает через OpenGL на FreeBSD!</p>"
|
||||
"<button onclick='document.getElementById(\"status\").innerText=\"✓ Готово!\"'>"
|
||||
"Нажми меня</button>"
|
||||
"<div id='status'>Система готова</div>"
|
||||
"</div></body></html>";
|
||||
|
||||
// Загружаем HTML
|
||||
SciterLoadHtml(g_sciter_hwnd, html, (UINT)strlen(html), "/");
|
||||
|
||||
printf("Sciter инициализирован и HTML загружен\n");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Обновление позиции окна Sciter
|
||||
// -------------------------------------------------------------
|
||||
void update_sciter_window_size(int width, int height) {
|
||||
if (g_sciter_hwnd) {
|
||||
RECT rect = {0, 0, width, height};
|
||||
//SciterSetWindowRect(g_sciter_hwnd, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Главный цикл обработки событий
|
||||
// -------------------------------------------------------------
|
||||
void main_loop(void) {
|
||||
XEvent ev;
|
||||
bool running = true;
|
||||
|
||||
printf("Композитор запущен. Нажмите Q или Escape для выхода.\n");
|
||||
|
||||
while (running) {
|
||||
// Обработка событий X11
|
||||
while (XPending(dpy) > 0) {
|
||||
XNextEvent(dpy, &ev);
|
||||
|
||||
switch (ev.type) {
|
||||
case Expose:
|
||||
if (ev.xexpose.count == 0) {
|
||||
// Обновляем Sciter при необходимости
|
||||
SciterUpdateWindow(g_sciter_hwnd);
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyPress:
|
||||
{
|
||||
char key_str[32];
|
||||
KeySym keysym;
|
||||
XLookupString(&ev.xkey, key_str, sizeof(key_str), &keysym, NULL);
|
||||
|
||||
// Выход по Q или Escape
|
||||
if (keysym == XK_q || keysym == XK_Q || keysym == XK_Escape) {
|
||||
printf("Выход по запросу пользователя\n");
|
||||
running = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ConfigureNotify:
|
||||
screen_width = ev.xconfigure.width;
|
||||
screen_height = ev.xconfigure.height;
|
||||
update_sciter_window_size(screen_width, screen_height);
|
||||
break;
|
||||
|
||||
case ClientMessage:
|
||||
if ((Atom)ev.xclient.data.l[0] == wm_delete_window) {
|
||||
printf("Получен WM_DELETE_WINDOW\n");
|
||||
running = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case DestroyNotify:
|
||||
printf("Окно разрушено\n");
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем Sciter
|
||||
SciterUpdateWindow(g_sciter_hwnd);
|
||||
|
||||
// Небольшая задержка для снижения нагрузки на CPU
|
||||
usleep(10000); // 10ms
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Очистка ресурсов
|
||||
// -------------------------------------------------------------
|
||||
void cleanup(void) {
|
||||
if (g_sciter_hwnd) {
|
||||
//SciterCloseWindow(g_sciter_hwnd);
|
||||
g_sciter_hwnd = NULL;
|
||||
}
|
||||
|
||||
if (glx_ctx) {
|
||||
glXMakeCurrent(dpy, None, NULL);
|
||||
glXDestroyContext(dpy, glx_ctx);
|
||||
glx_ctx = NULL;
|
||||
}
|
||||
|
||||
if (comp_win) {
|
||||
XDestroyWindow(dpy, comp_win);
|
||||
comp_win = 0;
|
||||
}
|
||||
|
||||
if (dpy) {
|
||||
XCloseDisplay(dpy);
|
||||
dpy = NULL;
|
||||
}
|
||||
|
||||
printf("Ресурсы освобождены\n");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Точка входа
|
||||
// -------------------------------------------------------------
|
||||
int main(int argc, char **argv) {
|
||||
// Игнорируем аргументы
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
printf("Запуск Calista композитора для FreeBSD\n");
|
||||
|
||||
init_x11();
|
||||
init_glx();
|
||||
init_sciter();
|
||||
|
||||
main_loop();
|
||||
cleanup();
|
||||
|
||||
return 0;
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#include <GL/glx.h>
|
||||
|
||||
Display *dpy;
|
||||
Window root_win;
|
||||
Window comp_win;
|
||||
int screen;
|
||||
int screen_width, screen_height;
|
||||
|
||||
GLXContext glx_ctx;
|
||||
GLXFBConfig fb_config;
|
||||
|
||||
Atom wm_delete_window;
|
||||
Atom net_active_window;
|
||||
Atom net_wm_state;
|
||||
Atom net_wm_state_fullscreen;
|
||||
Atom wm_state;
|
||||
pid_t firefox_pid = -1;
|
||||
|
||||
// Структура для хранения информации об окнах
|
||||
typedef struct ManagedWindow {
|
||||
Window id;
|
||||
Pixmap pixmap;
|
||||
int x, y, width, height;
|
||||
int mapped;
|
||||
struct ManagedWindow *next;
|
||||
} ManagedWindow;
|
||||
|
||||
ManagedWindow *windows = NULL;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 1. Инициализация X11 и создание полноэкранного окна композитора
|
||||
// -------------------------------------------------------------
|
||||
void init_x11() {
|
||||
dpy = XOpenDisplay(NULL);
|
||||
if (!dpy) {
|
||||
fprintf(stderr, "Не могу открыть дисплей\n");
|
||||
exit(1);
|
||||
}
|
||||
screen = DefaultScreen(dpy);
|
||||
root_win = DefaultRootWindow(dpy);
|
||||
screen_width = WidthOfScreen(DefaultScreenOfDisplay(dpy));
|
||||
screen_height = HeightOfScreen(DefaultScreenOfDisplay(dpy));
|
||||
|
||||
int event_base, error_base;
|
||||
if (!XCompositeQueryExtension(dpy, &event_base, &error_base)) {
|
||||
fprintf(stderr, "Расширение Composite отсутствует\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Создаём своё собственное окно на весь экран
|
||||
XSetWindowAttributes attrs;
|
||||
attrs.override_redirect = True;
|
||||
attrs.background_pixel = 0;
|
||||
attrs.border_pixel = 0;
|
||||
attrs.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask |
|
||||
SubstructureNotifyMask | SubstructureRedirectMask;
|
||||
|
||||
comp_win = XCreateWindow(dpy, root_win,
|
||||
0, 0, screen_width, screen_height, 0,
|
||||
CopyFromParent, InputOutput, CopyFromParent,
|
||||
CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask,
|
||||
&attrs);
|
||||
|
||||
// Устанавливаем свойства для оконного менеджера
|
||||
net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
|
||||
net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
|
||||
net_active_window = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
|
||||
wm_state = XInternAtom(dpy, "WM_STATE", False);
|
||||
|
||||
// Регистрируемся как оконный менеджер
|
||||
Atom wm_s0 = XInternAtom(dpy, "WM_S0", False);
|
||||
XSetSelectionOwner(dpy, wm_s0, comp_win, CurrentTime);
|
||||
|
||||
// Делаем окно композитора полноэкранным и поверх всех
|
||||
XChangeProperty(dpy, comp_win, net_wm_state, XA_ATOM, 32,
|
||||
PropModeReplace, (unsigned char *)&net_wm_state_fullscreen, 1);
|
||||
|
||||
wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(dpy, comp_win, &wm_delete_window, 1);
|
||||
|
||||
XMapWindow(dpy, comp_win);
|
||||
XRaiseWindow(dpy, comp_win);
|
||||
|
||||
// Скрываем курсор
|
||||
Cursor invisible_cursor;
|
||||
Pixmap bitmap = XCreateBitmapFromData(dpy, comp_win, "", 1, 1);
|
||||
XColor color = {0, 0, 0, 0, 0, 0};
|
||||
invisible_cursor = XCreatePixmapCursor(dpy, bitmap, bitmap, &color, &color, 0, 0);
|
||||
XDefineCursor(dpy, comp_win, invisible_cursor);
|
||||
XFreePixmap(dpy, bitmap);
|
||||
|
||||
XFlush(dpy);
|
||||
|
||||
printf("Окно композитора создано: 0x%lx (%dx%d) полноэкранный режим\n",
|
||||
comp_win, screen_width, screen_height);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 2. Инициализация GLX
|
||||
// -------------------------------------------------------------
|
||||
void init_glx() {
|
||||
XWindowAttributes win_attrs;
|
||||
XGetWindowAttributes(dpy, comp_win, &win_attrs);
|
||||
|
||||
int fb_attribs[] = {
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RED_SIZE, 8,
|
||||
GLX_GREEN_SIZE, 8,
|
||||
GLX_BLUE_SIZE, 8,
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
None
|
||||
};
|
||||
|
||||
int num_fb;
|
||||
GLXFBConfig *fb_configs = glXChooseFBConfig(dpy, screen, fb_attribs, &num_fb);
|
||||
if (!fb_configs || num_fb == 0) {
|
||||
fprintf(stderr, "Не удалось получить FBConfig\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fb_config = fb_configs[0];
|
||||
XFree(fb_configs);
|
||||
|
||||
glx_ctx = glXCreateNewContext(dpy, fb_config, GLX_RGBA_TYPE, NULL, True);
|
||||
if (!glx_ctx) {
|
||||
fprintf(stderr, "Не удалось создать GLX-контекст\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!glXMakeCurrent(dpy, comp_win, glx_ctx)) {
|
||||
fprintf(stderr, "Не удалось сделать контекст текущим\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("GLX инициализирован. OpenGL: %s\n", glGetString(GL_VERSION));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 3. Запуск Firefox
|
||||
// -------------------------------------------------------------
|
||||
void start_firefox() {
|
||||
firefox_pid = fork();
|
||||
|
||||
if (firefox_pid == -1) {
|
||||
perror("fork");
|
||||
return;
|
||||
}
|
||||
|
||||
if (firefox_pid == 0) {
|
||||
// Даем время на инициализацию
|
||||
sleep(1);
|
||||
|
||||
// Запускаем Firefox с параметрами для правильного отображения
|
||||
execlp("firefox", "firefox",
|
||||
"-kiosk", "about:blank",
|
||||
"-width", "800", "-height", "600",
|
||||
NULL);
|
||||
|
||||
perror("execlp");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("Firefox запущен с PID: %d\n", firefox_pid);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 4. Управление окнами
|
||||
// -------------------------------------------------------------
|
||||
void maximize_window(Window win) {
|
||||
// Отправляем событие ConfigureRequest для максимизации окна
|
||||
XEvent ev;
|
||||
|
||||
ev.xconfigurerequest.type = ConfigureRequest;
|
||||
ev.xconfigurerequest.display = dpy;
|
||||
ev.xconfigurerequest.window = win;
|
||||
//ev.xconfigurerequest.root = root_win;
|
||||
ev.xconfigurerequest.x = 0;
|
||||
ev.xconfigurerequest.y = 0;
|
||||
ev.xconfigurerequest.width = screen_width;
|
||||
ev.xconfigurerequest.height = screen_height;
|
||||
ev.xconfigurerequest.border_width = 0;
|
||||
ev.xconfigurerequest.above = None;
|
||||
ev.xconfigurerequest.detail = Above;
|
||||
ev.xconfigurerequest.value_mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
|
||||
|
||||
XSendEvent(dpy, root_win, False, SubstructureRedirectMask | SubstructureNotifyMask, &ev);
|
||||
XFlush(dpy);
|
||||
|
||||
printf("Отправлена команда максимизации для окна 0x%lx\n", win);
|
||||
}
|
||||
|
||||
void add_window(Window win) {
|
||||
// Проверяем, не окно ли это композитора
|
||||
if (win == comp_win) return;
|
||||
|
||||
// Проверяем, не отслеживаем ли уже
|
||||
ManagedWindow *current = windows;
|
||||
while (current) {
|
||||
if (current->id == win) return;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// Получаем атрибуты окна
|
||||
XWindowAttributes attrs;
|
||||
if (!XGetWindowAttributes(dpy, win, &attrs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Игнорируем окна без карты
|
||||
if (attrs.map_state != IsViewable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Включаем композитный режим
|
||||
XCompositeRedirectWindow(dpy, win, CompositeRedirectAutomatic);
|
||||
|
||||
// Получаем Pixmap
|
||||
Pixmap pixmap = XCompositeNameWindowPixmap(dpy, win);
|
||||
if (!pixmap) {
|
||||
printf("Не удалось получить pixmap для окна 0x%lx\n", win);
|
||||
return;
|
||||
}
|
||||
|
||||
// Добавляем в список
|
||||
ManagedWindow *new_win = malloc(sizeof(ManagedWindow));
|
||||
new_win->id = win;
|
||||
new_win->pixmap = pixmap;
|
||||
new_win->x = attrs.x;
|
||||
new_win->y = attrs.y;
|
||||
new_win->width = attrs.width;
|
||||
new_win->height = attrs.height;
|
||||
new_win->mapped = 1;
|
||||
new_win->next = windows;
|
||||
windows = new_win;
|
||||
|
||||
printf("Добавлено окно 0x%lx: размер %dx%d, позиция (%d,%d)\n",
|
||||
win, attrs.width, attrs.height, attrs.x, attrs.y);
|
||||
|
||||
// Максимизируем окно, если это Firefox или другое приложение
|
||||
// Получаем имя окна для проверки
|
||||
char *window_name = NULL;
|
||||
XFetchName(dpy, win, &window_name);
|
||||
if (window_name) {
|
||||
printf("Имя окна: %s\n", window_name);
|
||||
XFree(window_name);
|
||||
}
|
||||
|
||||
// Максимизируем все нормальные окна
|
||||
maximize_window(win);
|
||||
}
|
||||
|
||||
void remove_window(Window win) {
|
||||
ManagedWindow **current = &windows;
|
||||
while (*current) {
|
||||
if ((*current)->id == win) {
|
||||
ManagedWindow *to_remove = *current;
|
||||
*current = (*current)->next;
|
||||
|
||||
if (to_remove->pixmap) {
|
||||
XFreePixmap(dpy, to_remove->pixmap);
|
||||
}
|
||||
free(to_remove);
|
||||
|
||||
printf("Удалено окно 0x%lx\n", win);
|
||||
return;
|
||||
}
|
||||
current = &(*current)->next;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 5. Отрисовка
|
||||
// -------------------------------------------------------------
|
||||
void render_all_windows() {
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
ManagedWindow *current = windows;
|
||||
|
||||
// Временное решение: рисуем цветные прямоугольники вместо текстур
|
||||
// Для реальной работы нужно использовать текстуры из pixmap
|
||||
while (current) {
|
||||
// Рисуем рамку окна
|
||||
glColor3f(0.3f, 0.5f, 0.8f);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(current->x, current->y);
|
||||
glVertex2i(current->x + current->width, current->y);
|
||||
glVertex2i(current->x + current->width, current->y + current->height);
|
||||
glVertex2i(current->x, current->y + current->height);
|
||||
glEnd();
|
||||
|
||||
// Рисуем границу
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glVertex2i(current->x, current->y);
|
||||
glVertex2i(current->x + current->width, current->y);
|
||||
glVertex2i(current->x + current->width, current->y + current->height);
|
||||
glVertex2i(current->x, current->y + current->height);
|
||||
glEnd();
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
glXSwapBuffers(dpy, comp_win);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 6. Обработка событий
|
||||
// -------------------------------------------------------------
|
||||
void handle_map_request(XMapRequestEvent *ev) {
|
||||
printf("MapRequest для окна 0x%lx\n", ev->window);
|
||||
if (ev->window != comp_win) {
|
||||
XMapWindow(dpy, ev->window);
|
||||
add_window(ev->window);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_map_notify(XMapEvent *ev) {
|
||||
printf("MapNotify для окна 0x%lx\n", ev->window);
|
||||
if (ev->window != comp_win) {
|
||||
add_window(ev->window);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_unmap_notify(XUnmapEvent *ev) {
|
||||
printf("UnmapNotify для окна 0x%lx\n", ev->window);
|
||||
if (ev->window != comp_win) {
|
||||
remove_window(ev->window);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_configure_request(XConfigureRequestEvent *ev) {
|
||||
if (ev->window == comp_win) return;
|
||||
|
||||
XWindowChanges changes;
|
||||
changes.x = ev->x;
|
||||
changes.y = ev->y;
|
||||
changes.width = ev->width;
|
||||
changes.height = ev->height;
|
||||
changes.border_width = ev->border_width;
|
||||
changes.sibling = ev->above;
|
||||
changes.stack_mode = ev->detail;
|
||||
|
||||
// Принудительно устанавливаем полноэкранный режим для всех окон
|
||||
changes.width = screen_width;
|
||||
changes.height = screen_height;
|
||||
changes.x = 0;
|
||||
changes.y = 0;
|
||||
|
||||
XConfigureWindow(dpy, ev->window,
|
||||
CWX | CWY | CWWidth | CWHeight | CWBorderWidth |
|
||||
(ev->value_mask & (CWSibling | CWStackMode)),
|
||||
&changes);
|
||||
|
||||
printf("ConfigureRequest для 0x%lx: установлен размер %dx%d\n",
|
||||
ev->window, screen_width, screen_height);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 7. Главный цикл
|
||||
// -------------------------------------------------------------
|
||||
void main_loop() {
|
||||
XEvent ev;
|
||||
|
||||
// Подписываемся на события
|
||||
XSelectInput(dpy, root_win,
|
||||
SubstructureNotifyMask |
|
||||
SubstructureRedirectMask |
|
||||
PropertyChangeMask);
|
||||
|
||||
// Запускаем Firefox
|
||||
start_firefox();
|
||||
|
||||
printf("Композитор запущен. Нажмите Q для выхода.\n");
|
||||
printf("Размер экрана: %dx%d\n", screen_width, screen_height);
|
||||
|
||||
// Первоначальная отрисовка
|
||||
render_all_windows();
|
||||
|
||||
while (1) {
|
||||
XNextEvent(dpy, &ev);
|
||||
|
||||
switch (ev.type) {
|
||||
case MapRequest:
|
||||
handle_map_request(&ev.xmaprequest);
|
||||
render_all_windows();
|
||||
break;
|
||||
|
||||
case MapNotify:
|
||||
handle_map_notify(&ev.xmap);
|
||||
render_all_windows();
|
||||
break;
|
||||
|
||||
case UnmapNotify:
|
||||
handle_unmap_notify(&ev.xunmap);
|
||||
render_all_windows();
|
||||
break;
|
||||
|
||||
case ConfigureRequest:
|
||||
handle_configure_request(&ev.xconfigurerequest);
|
||||
render_all_windows();
|
||||
break;
|
||||
|
||||
case Expose:
|
||||
if (ev.xexpose.count == 0) {
|
||||
render_all_windows();
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyPress:
|
||||
if (ev.xkey.keycode == 24) { // Q
|
||||
printf("Выход по Q\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case ClientMessage:
|
||||
if ((Atom)ev.xclient.data.l[0] == wm_delete_window) {
|
||||
printf("WM_DELETE_WINDOW\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 8. Очистка
|
||||
// -------------------------------------------------------------
|
||||
void cleanup() {
|
||||
// Удаляем все окна
|
||||
while (windows) {
|
||||
remove_window(windows->id);
|
||||
}
|
||||
|
||||
glXMakeCurrent(dpy, None, NULL);
|
||||
if (glx_ctx) glXDestroyContext(dpy, glx_ctx);
|
||||
if (comp_win) XDestroyWindow(dpy, comp_win);
|
||||
if (dpy) XCloseDisplay(dpy);
|
||||
|
||||
if (firefox_pid > 0) {
|
||||
kill(firefox_pid, SIGTERM);
|
||||
waitpid(firefox_pid, NULL, WNOHANG);
|
||||
}
|
||||
|
||||
printf("Ресурсы освобождены\n");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
int main() {
|
||||
init_x11();
|
||||
init_glx();
|
||||
main_loop();
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user