GTK with Broadway backend 代码分析

根据官方文档可知,GTK的Broadway后端可以支持在浏览器中显示GTK应用程序。使用步骤也很简单,打开一个命令行窗口,执行:

broadwayd :5
//这里":5"也可以省略,默认为":0",数字表示Broadway display number,决定了使用的端口号。

打开浏览器,输入地址:127.0.0.1:8085 (端口8085 = 8080 + 5)。再打开一个命令行窗口,执行:

export GDK_BACKEND=broadway 
export BROADWAY_DISPLAY=:5  //这里的 ":5"的设置和前一个命令行窗口要保持一致
./your_applicaion

就看到在浏览器中打开了我们的应用程序。

代码地址:https://github.com/gooroom/gtk3

执行 broadwayd :5 的代码来自gdk/broadway/broadwayd.c

在main函数里,解析输入的参数,创建BroadwayServer类型的server、创建新的listener,并添加监听。

当主循环运行时,开始接受添加的socket连接。

g_signal_connect (listener, "incoming", G_CALLBACK (incoming_client), NULL); 将信号“incoming”和回调函数incoming_client() 联系起来,在另一端启动一个gtk application时会进入函数。incoming_client()会创建新的client并将数据异步读入stream的缓冲区:g_buffered_input_stream_fill_async (client->in, 4*1024, 0, NULL, client_fill_cb, client);

client_fill_cb()函数判断异步读取是否完成,如果成功,处理请求:client_handle_request (client, (BroadwayRequest *)buffer); 这里会根据request的type执行不同的函数,比如创建新的surface、server flush、surface show等。这些request 执行完成后,会根据需要发送到WebClient端进行渲染显示。

同时,broadwayd还和web client 建立连接,在创建BroadwayServer时:

g_signal_connect (server->service, "incoming", G_CALLBACK (handle_incoming_connection), NULL);  将信号 ”incoming“ 和 handle_incoming_connection() 联系起来,当打开浏览器时触发进入。获取并解析来自浏览器的 http request 以及 input ,同时还要将画面和其他输出发送到浏览器端。

这里用到了Gio lib 

Gio is a library providing useful classes for general purpose I/O, networking, IPC, settings, and other high level application functionality.

 

 

 

在启动应用程序时,调用gtk_init() (gtk/gtkmain.c)时,会gdk_display_open_default(),一路会走到gdk_display_manager_open_display() (gdk/gdkdisplaymanager.c),这里会读取环境变量获取使用的backend :backend_list = g_getenv ("GDK_BACKEND"),如果读取到的内容可以和gtk支持的backend名称匹配,执行此backend的 open_display() 方法。

如果我们之前执行了export GDK_BACKEND=broadway ,会对应走到_gdk_broadway_display_open()(gdk/broadway/gdkdisplay-broadway.c) 。这里也会创建GdkBroadwayServer 类型的server :_gdk_broadway_server_new (display, display_name, &error); 其中有一个需要创建的成员是connection = g_socket_client_connect()。会发现,如果一开始两个命令行里“:5”这部分如果不一致,connection会创建失败,打印出“Unable to init Broadway server”。

至此,gtk_init()执行完成,broadway server创建成功,返回display。

创建一个应用程序时都会用到gtk_widget_show() (gtk/gtkwidget.c),以这个函数为例,调用顺序:

gtk_widget_show -> gtk_widget_real_show -> gtk_widget_map 然后再分别执行下面两步

1、gtk_widget_realize  -> gtk_widget_real_realize 

2、gtk_widget_real_map -> gdk_window_show 

最终会调到gdk_window_show() (gdk/gdkwindow.c) ,里面会调用impl_class->show(),对应函数在不同的backend下有不同的实现。

static void
gdk_window_show_internal (GdkWindow *window, gboolean raise)
{
    ....
  if (gdk_window_has_impl (window) && (was_viewable || !did_show))
    {
      impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
      impl_class->show (window, !did_show ? was_mapped : TRUE);
    }
    ....
}

gdk/broadway/gdkwindow-broadway.c 中 gdk_window_impl_broadway_class_init() 会注册函数。(同样在gdk/x11/gdkwindow-x11.c 下也有相应的注册函数)以impl_class->show = gdk_window_broadway_show; 为例,最终会调用gdk_broadway_server_send_message_with_size() (gdk/broadway/gdkbroadway-server.c) ,这里有一个参数是request的type,broadwayd 端会根据接收到的type类型进行不同的处理。

再调用queue_flush() (gdk/broadway/gdkwindow-broadway.c)

gboolean
_gdk_broadway_server_window_show (GdkBroadwayServer *server,
				  gint id)
{
  BroadwayRequestShowWindow msg;

  msg.id = id;
  gdk_broadway_server_send_message (server, msg,
				    BROADWAY_REQUEST_SHOW_WINDOW);  // request type
  
  return TRUE;
}

static guint32
gdk_broadway_server_send_message_with_size (GdkBroadwayServer *server, BroadwayRequestBase *base,
					    gsize size, guint32 type)
{
  GOutputStream *out;
  gsize written;

  base->size = size;
  base->type = type;
  base->serial = server->next_serial++;

  out = g_io_stream_get_output_stream (G_IO_STREAM (server->connection));

  if (!g_output_stream_write_all (out, base, size, &written, NULL, NULL))
    {
      g_printerr ("Unable to write to server\n");
      exit (1);
    }

  g_assert (written == size);

  return base->serial;
}

/* We need to flush in an idle rather than AFTER_PAINT, as the clock
   is frozen during e.g. window resizes so the paint will not happen
   and the window resize request is never flushed. */
static void
queue_flush (GdkWindow *window)
{
  if (flush_id == 0)
    {
      flush_id = gdk_threads_add_idle (flush_idle, NULL);
      g_source_set_name_by_id (flush_id, "[gtk+] flush_idle");
    }
}

broadwayd端在处理了多个消息后,才会通过 g_output_stream_write_all 将header、buf 发送出去,web端 onmessage() 接收到消息后再逐命令处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值