Обновить wm/x11/src/main.c

This commit is contained in:
svsptech 2026-04-24 02:47:20 +05:00
parent 2b92a105e0
commit 96b2a979c9

View File

@ -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;
}