Такую возможность предоставляет Broadway — уже давно не новый, но остающийся в тени backend для GTK3, позволяющий привнести новые возможности туда, где казалось бы уже все давно протоптано.
![](http://habrastorage.org/getpro/habr/post_images/693/08c/31b/69308c31bfb15f6fe4374294c8804876.png)
Что такое 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 сервера на хосте. Достаточно наличия нужных библиотек. Приложение будет отображаться в браузере практически так же, как и в стандартном режиме. Сравним:
![](http://habrastorage.org/getpro/habr/post_images/b6d/dd9/936/b6ddd99362ca660e9a46fef1e83ec995.png)
![](http://habrastorage.org/getpro/habr/post_images/a62/caf/3e6/a62caf3e6f160aa7b33d45faba32b50f.png)
Основные нюансы использования
Первое, что бросается в глаза — отсутствует имя окна, а так же заголовок страницы гласит «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):
![](http://habrastorage.org/getpro/habr/post_images/efe/a68/b88/efea68b88e08c9f22863bcc820192588.png)
Если раньше (до 3.8) пользователю следовало выключать приложение самостоятельно, иначе если закрыть браузер или зайти с другого места, все что было видно — белый фон. То теперь broadway берет работу с сокетами и idle/disconnect на себя. Теперь надо разобраться с другими мелочами
Немного кастомизации broadwayd
![](http://habrastorage.org/getpro/habr/post_images/8df/a98/8f3/8dfa988f3a4d3e081f21b92e6b80f917.png)
К счастью, 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"
![](http://habrastorage.org/getpro/habr/post_images/d80/ccc/dcd/d80cccdcdb1976288d36e1541c9e6727.png)
Можно изменить размер экрана по умолчанию, может быть актуально при использовании с мобильными устройствами.
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 и возможно кто-то сможет решить свою задачу боле простыми методами.
ссылка на оригинал статьи http://habrahabr.ru/post/212705/
Добавить комментарий