diff --git a/wm/x11/src/main.c b/wm/x11/src/main.c index 2d39411..99d3557 100644 --- a/wm/x11/src/main.c +++ b/wm/x11/src/main.c @@ -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 -#include -#include -#include - -// X11, OpenGL и FreeBSD специфичные заголовки -#include -#include -#include -#include -#include - -#include - -// Sciter C API (не C++!) -#include - -// Глобальные переменные 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 = - "" - "" - "

Calista + Sciter

" - "

Sciter работает через OpenGL на FreeBSD!

" - "" - "
Система готова
" - "
"; - - // Загружаем 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; } \ No newline at end of file