Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)

в 15:13, , рубрики: GTK+, html5, web, Программирование, метки: , , ,

Иногда необходимо предоставить доступ к приложениям которые не всегда есть возможность установить локально, да и не всегда это нужно. Наверное, лучшим выходом тут был бы web интерфейс на JS/PHP и иже с ними. Но возможно есть другие, более простые в некоторых случаях пути? Особенно если приложение должно оставаться портативным, а ещё лучше не делать почти ничего дополнительно в коде для реализации такого функционала.
Такую возможность предоставляет Broadway — уже давно не новый, но остающийся в тени backend для GTK3, позволяющий привнести новые возможности туда, где казалось бы уже все давно протоптано.

Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)

Что такое GTK Broadway

Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)
Про broadway рассказывали на хабре, аж в 2011 году. Однако, мало что поменялось с тех пор в области освещения данной опции.
Основной идеей является написание одной единственной версии кода на базе обычного и уже привычного GTK3, который может одновременно и практически без изменений работать как классическое графическое приложение, а так же рендерить свой интерфейс посредством HTML5 и websockets в браузере. В версиях 3.8+ появилась возможность поставить пароль на подключение и возможность запуска множества приложений на одном сервере.

Какая версия GTK3?

Официально Broadway зарелизили вместе с GTK3, но только начиная с версии 3.8 данная подсистема избавилась от обидных ошибок. Я использую 3.10.7, так как в ней поменяли прицнип использования, исправили много ошибок и вынесли HTTP сервер как отдельное приложение. Поэтому рассказывать буду про 3.10, ибо все равно к нему всё придет.

Принцип работы

Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)
Вместе с GTK3 устанавливается HTTP сервер интегрированный с GTK3 (broadwayd). При запуске он создает сокет, к примеру /run/user/1000/broadway1.socket и ждет подключения приложения на GTK3 к этому сокету.
Можно указать иной порт (номер экрана), можно задать пароль на подключение ( >= GTK 3.8).

Зачем это нужно

Подобный режим работы не претендует на замену ставшим теперь стандартными интерфейсам на базе PHP/JS/Java и иже с ними. Но таким образом можно создать службу, например в виде виртуальной машины, которая будет предоставлять пользователям доступ к каким-либо вычислительным службам или утилитам без траты времени на разработку специального интерфейса, при этом обеспечив высокую производительность на стороне сервера. Я, к примеру, на нем делал консоль доступа к виртуальным машинам XenServer и сейчас реализую удаленный доступ к камерам своего телескопа.

Получение Broadway

На данный момент, насколько мне известно, ни один дистрибутив из тех, что я пробовал, не предоставляет пакет GTK3 + broadway в стабильных ветках. Debian 7 имеет такой пакет в experimental репозитории, но вроде и с ним не все гладко.
В Debian based системах можно добавить PPA собранный добрым человеком (Nicolas Delvaux)
Есть бэкпорт сделанный им же на базе 3.8.0

Оба варианта использовать надо с осторожностью и пониманием, ибо есть реальная возможность основательно поломать систему. Я же использую 3.10.7, тут уже только из исходников.

Краткая инструкция по сборке

Собираем, как описано в мануале к LFS не забывая про checkinstall вместо make install если у вас есть пакетный менеджер
К сожалению, там не описан важный нюанс — помимо сборки и установки самой библиотеки GTK3, необходимо вручную собрать и скопировать broadwayd куда-нибудь, доступное через $PATH, например в /usr/sbin

cd gtk+-3.10.7/gdk/broadway
make clean
make
cp broadwayd /usr/sbin

Запуск

Если запустить приложение так

GDK_BACKEND=broadway BROADWAY_DISPLAY=:0 ./gtk_app

,
то оно будет работать в фоне, как web сервер, а доступ к интерфейсу мы получаем, зайдя в браузере по соответствующему адресу и порту. В данном примере это будет localhost:8080 (Порт вычисляется как port = 8080 + (display — 1)). Веб сервер уже идет в поставке с GTK — broadwayd. При этом даже нет необходимости наличия работающего X сервера на хосте. Достаточно наличия нужных библиотек. Приложение будет отображаться в браузере практически так же, как и в стандартном режиме. Сравним:

Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)

Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)

Основные нюансы использования

Первое, что бросается в глаза — отсутствует имя окна, а так же заголовок страницы гласит «broadway 2.0», так же невозможно вручную изменить размер окна перетягиванием.

gtk_status_icon_set_visible(GTK_STATUS_ICON(tray_icon), TRUE);

то это бы вызвало segfault. Следующий нюанс — все, что выполняется в браузере, делается относительно машины хоста. К примеру, выбрать файл на локальной браузеру машине и что-то с ним сделать на стороне приложения не выйдет. Или вызов libnotify приведет к появлению всплывающего окна на хосте, а не на машине с браузером.
С другой стороны, открываются другие плюсы вроде упрощенного доступа к ресурсам сервера, но необходимо уделять внимание безопасности, например через gtk_file_chooser_set_local_only, gtk_file_chooser_set_filter и настроить jail для пользователя отдельного и запускать веб версию под этим пользователем, иначе у пользователя все будет как на ладони, по крайней мере структура директорий.

Другая проблема в том, мышкой в браузере в таком приложении можно попасть в стандартное меню, а вот пальцем — не очень (если заходим с мобильного устройства). Кроме того, размеры и положение окна не везде одинаковы — это тоже надо будет учесть. И очень неприятное — GTK3 не поддерживает single click. Так что выбрать другую директорию мне так и не удалось, даже выключив double tap (Android 4.2.2/Firefox/Chrome). Зайти одновременно на одну и ту же сессию с разных машин/браузеров не выйдет, так как сокет один, предыдущая сессия будет автоматически закрыта

Практика

Рассмотрим, как можно учесть возможность использования Broadway в приложении.
Будем использовать Glade как конструктор интерфейса. Я использую две версии интерфейса в одном приложении: одна для обычного использования и одна — браузерная, которая старается учитывать нюансы работы в браузере и/или на мобильном устройстве.
GTK позволяет подгружать интерфейс из буфера памяти, чем и воспользуемся. В Makefile я добавляю преобразование из XML файла, в котором сохраняет интерфейс Glade, в массив uint8_t, который и будет подгружать приложение. Это позволяет хранить все в одном единственном исполняемом файле.

all:	
	xxd -i desktop.glade ../src/desktop.h;
	xxd -i web.glade ../src/web.h;	
	make -C ../src
clean:
	make -C ../src clean

Теперь у нас в заголовочном файле массив с интерфейсом. Основной цикл стандартен.

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);
    GtkWidget *main_window = glade_init( );
    gtk_widget_show(main_window); 
    gtk_main( );
}

Заранее отвечу про return — собирается с std=c99
Далее нам нужно понять, когда использовать тот или иной вид интерфейса.
Здесь нам поможет то, что в случае если используется Broadway, то GDK экран называется «browser»

#ifdef __WIN32
G_MODULE_EXPORT
#endif
gboolean is_run_in_a_browser (void)
{   
   GdkScreen *current_screen = gdk_screen_get_default();   
   char *screen_name= gdk_screen_make_display_name(current_screen);
   gboolean is_browser = !strcmp(screen_name,"browser"); 
   free(screen_name);
   return is_browser;
}

ifdef тут нужен для адекватной работы под Win32, если понадобится.
В данной функции мы проверяем имя GDK окна, и если это браузер, то заодно убираем оформление (рамку) окна. Теперь подгрузим интерфейс в зависимости от того, через broadway запущено приложение или нет

#ifdef __WIN32
G_MODULE_EXPORT
#endif
GtkWidget *glade_init(void)
{        
    GtkBuilder *builder = gtk_builder_new( );
    GError *error = NULL;       
    gboolean web_run =  is_run_in_a_browser();
    gtk_builder_add_from_string(builder, web_run ? (char *)web_glade : (char *)desktop_glade, -1, &error);
    if (!error)
    {
        printf("Couldn't load builder buffer: %s", error->message);
        g_error_free(error);
        return NULL;
    }    
    gtk_builder_connect_signals(builder, NULL );    
    GtkWidget *main_window = GTK_WIDGET (gtk_builder_get_object (builder, "mainwin"));        
    g_object_unref(builder);
    return ( main_window );
}

Запускаем приложение. Обнаруживаем, что установки расположения окна не работают. Сразу после запуска разрешение GDK screen 1024*768 — это рассмотрим позже. Кроме того, центровать окно придется руками, так как редко разрешение браузера совпадет с значением по умолчанию в broadway.
После того, как мы открыли приложение в браузере, нам необходимо выставить нужное разрешение экрана (чаще всего это будет растянуть на весь экран) и при этом совместить верхний левый угол с началом координат. Сделать это можно, например, так.

gtk_window_move(GTK_WINDOW(main_window),0,0);   
GdkScreen *current_screen = gdk_screen_get_default();
int32_t w = gdk_screen_get_width(current_screen);
int32_t h = gdk_screen_get_height(current_screen);        
gtk_window_resize(GTK_WINDOW(main_window),w, h);

Тогда тестовое приложение (VNC консоль) будет выглядеть вот так в Android/Firefox):

Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)

Если раньше (до 3.8) пользователю следовало выключать приложение самостоятельно, иначе если закрыть браузер или зайти с другого места, все что было видно — белый фон. То теперь broadway берет работу с сокетами и idle/disconnect на себя. Теперь надо разобраться с другими мелочами

Немного кастомизации broadwayd

Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)

К счастью, GTK3 это opensource, так что можно залезть поглубже и кое-что поменять.

Начнем с заголовка окна.

Текст по умолчанию, «broadway 2.0», вряд ли кого-то устроит, изменим это. HTML5 страница сервера состоит из шаблона и JS файла. Заголовок страницы прописан в стандартном хедере HTML:

<title>broadway 2.0</title>

При сборке страница конвертируется perl скриптом в простой C массив, который хранится в gtk+-3.10.7/gdk/broadway/clienthtml.h — static const char client_html[].
Далее все просто, gtk+-3.10.7/gdk/broadway/broadway-server.c:

static void
got_request (HttpRequest *request)
...............
if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0)
    send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html) - 1);

Изменим немного алгоритм работы, будем использовать хэдер как формат для sprintf. Поэтому gtk+-3.10.7/gdk/broadway/clienthtml.h придется немного поправить — добавить экранирование знаков процента, например как тут:

background-image: -moz-linear-gradient(#D1D2D2 0%%, #BABBBC 65%%, #D4D4D5 100%%);n"

вместо

background-image: -moz-linear-gradient(#D1D2D2 0%, #BABBBC 65%, #D4D4D5 100%);n"

И заменим текст заголовка

<title>broadway 2.0</title>

на спецификатор

<title>%s</title>

Изменим алгоритм работы самого сервера:

char *http_title = getenv("GTK_HTTP_TITLE"); 
if (NULL == http_title)
{
  http_title = "Default";
}
size_t total_html_size = sizeof client_html + strlen(http_title) + 1;
char *_client_html = malloc (total_html_size);
snprintf(_client_html, total_html_size, client_html, http_title);  
if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0)
  send_data (request, "text/html", _client_html, strlen(_client_html) - 1);
...........
g_free (_client_html);

Можно было бы использовать asprintf, но тогда пришлось бы модернизировать Makefile, чего мне делать не хотелось.
Пересобираем broadwayd, зададим глобальную переменную и запускаем сервер

export GTK_HTTP_TITLE="PAGE TITLE"
Broadway — рендеринг интерфейса GTK3 в браузере (HTML5)

Можно изменить размер экрана по умолчанию, может быть актуально при использовании с мобильными устройствами.

static void
gdk_broadway_screen_init (GdkBroadwayScreen *screen)
{
  screen->width = 1024;
  screen->height = 768;
}

И изменить имя GDK экрана, таким образом можно, к примеру, создать несколько специфических версий broadwayd и идентифицировать их таким образом

static gchar *
gdk_broadway_screen_make_display_name (GdkScreen *screen)
{
  return g_strdup ("browser");
}

static gchar *
gdk_broadway_screen_get_monitor_plug_name (GdkScreen *screen,
					   gint       monitor_num)
{
  return g_strdup ("browser");
}

Заключение

Broadway — действительно интересная фича GTK3, позволяющая быстро создавать интересные легкие сервисы для определенного круга задач. Но, к сожалению, практически не используемый. Данной статьей я хотел обратить внимание бОльшего количества людей на broadway и возможно кто-то сможет решить свою задачу боле простыми методами.

Автор: Pugnator

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js