...

среда, 19 февраля 2014 г.

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

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

Такую возможность предоставляет Broadway — уже давно не новый, но остающийся в тени backend для GTK3, позволяющий привнести новые возможности туда, где казалось бы уже все давно протоптано.




Что такое GTK Broadway




Про broadway рассказывали на хабре, аж в 2011 году. Однако, мало что поменялось с тех пор в области освещения данной опции.

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


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




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

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




Вместе с 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 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):

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


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



К счастью, 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"



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



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 и возможно кто-то сможет решить свою задачу боле простыми методами.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий