1. 程式人生 > >spice-gtk-0.35原始碼解析之spicy解析

spice-gtk-0.35原始碼解析之spicy解析

 

spice-gtk介紹:

spice-gtk是基於紅帽的spice遠端連線協議的一套客戶端核心原始碼,上層有一套virt-viewer也是客戶端原始碼,只不過spice-gtk屬於底層直接對話協議的專案,而virt-viewer是基於spice-gtk的更加偏向於介面的專案。

spicy:

spicy是基於spice-gtk專案的一個測試可執行檔案(virt-viewer的可執行檔案remote-viewer是更加前端的連線客戶端),此命令可以直接連線遠端桌面,首先給一下spicy的使用方法:

spicy -h 192.168.112.32 -p 61000 -w 123

其中ip埠和密碼都是服務端建立虛擬機器時已經設定好的(在此不說服務端),spicy原始碼在spice-gtk/tools/spicy.c下面,我們可以從這裡作為頭,開始慢慢滲透spice-gtk專案。下面是spicy的主函式入口:

int main(int argc, char *argv[])
{
    GError *error = NULL;
    GOptionContext *context;
    spice_connection *conn;
    gchar *conf_file, *conf;
    char *host = NULL, *port = NULL, *tls_port = NULL, *unix_path = NULL;

    keyfile = g_key_file_new();//keyfile是一個全域性變數,我們將配置檔案讀到keyfile裡面,再在程式裡面讀取儲存的配置檔案

    int mode = S_IRWXU;
    conf_file = g_build_filename(g_get_user_config_dir(), "spicy", NULL);//g_get_user_config_dir()這個在linux下是/root/.config

    if (g_mkdir_with_parents(conf_file, mode) == -1)
        SPICE_DEBUG("failed to create config directory");
    g_free(conf_file);

    conf_file = g_build_filename(g_get_user_config_dir(), "spicy", "settings", NULL);
    if (!g_key_file_load_from_file(keyfile, conf_file,
                                   G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
        SPICE_DEBUG("Couldn't load configuration: %s", error->message);
        g_clear_error(&error);
    }

    /* parse opts */
    gtk_init(&argc, &argv);
#if HAVE_GSTAUDIO || HAVE_GSTVIDEO
    gst_init(&argc, &argv);
#endif
    context = g_option_context_new("- spice client test application");
    g_option_context_set_summary(context, "Gtk+ test client to connect to Spice servers.");
    g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
    g_option_context_add_group(context, spice_get_option_group());
    g_option_context_set_main_group(context, spice_cmdline_get_option_group());
    g_option_context_add_main_entries(context, cmd_entries, NULL);
    g_option_context_add_group(context, gtk_get_option_group(TRUE));
#if HAVE_GSTAUDIO || HAVE_GSTVIDEO
    g_option_context_add_group(context, gst_init_get_option_group());
#endif
    if (!g_option_context_parse (context, &argc, &argv, &error)) {
        g_print("option parsing failed: %s\n", error->message);
        exit(1);
    }
    g_option_context_free(context);

    if (version) {
        g_print("spicy " PACKAGE_VERSION "\n");
        exit(0);
    }

    /*從這裡開始上面其實都不用看,上面keyfile是讀配置檔案的能記住上次連線的ip和埠什麼的,option_context是spicy的引數列表,執行前命令列敲打spicy --help顯示的東西,下面才是真正的開始*/

    mainloop = g_main_loop_new(NULL, false);//mainloop是個全域性變數,先建立變數但是先不阻塞直到g_main_loop_run的時候程式阻塞

    conn = connection_new();
    spice_set_session_option(conn->session);
    spice_cmdline_session_setup(conn->session);

    g_object_get(conn->session,
                 "unix-path", &unix_path,
                 "host", &host,
                 "port", &port,
                 "tls-port", &tls_port,
                 NULL);
    /* If user doesn't provide hostname and port, show the dialog window
       instead of connecting to server automatically */
    if ((host == NULL || (port == NULL && tls_port == NULL)) && unix_path == NULL) {
        if (!spicy_connect_dialog(conn->session)) {
            exit(0);
        }
    }
    g_free(host);
    g_free(port);
    g_free(tls_port);
    g_free(unix_path);

    connection_connect(conn);
    if (connections > 0)
        g_main_loop_run(mainloop);//程式阻塞,在connection_destroy函式執行的時候會解開,程式就會往下走
    g_main_loop_unref(mainloop);

    /*寫配置檔案,從keyfile到conf_file*/
    if ((conf = g_key_file_to_data(keyfile, NULL, &error)) == NULL ||
        !g_file_set_contents(conf_file, conf, -1, &error)) {
        SPICE_DEBUG("Couldn't save configuration: %s", error->message);
        g_error_free(error);
        error = NULL;
    }

    g_free(conf_file);
    g_free(conf);
    g_key_file_free(keyfile);

    g_free(spicy_title);

    setup_terminal(true);
#if HAVE_GSTAUDIO || HAVE_GSTVIDEO
    gst_deinit();
#endif
    return 0;
}

從程式碼中間開始上面其實都不用看,上面keyfile是讀配置檔案的能記住上次連線的ip和埠什麼的,option_context是spicy的引數列表,執行前命令列敲打spicy --help顯示的東西,下面才是真正的開始,程式碼註釋有解釋

mainloop = g_main_loop_new(NULL, false); 這裡是建立一個迴圈物件,false表示未開始迴圈,一旦執行g_main_loop_run(mainloop);,程式就會進入睡眠狀態,所有的介面程式都會有這樣的操作,主程式睡眠,等待觸發的是介面上的訊號和相應的方法。

conn = connection_new();

//初始化一個連線的物件,裡面包括另一個物件session(所有的連線引數都在這個物件裡面)
           spice_set_session_option(conn->session);//設定一些session中的額外的引數
           spice_cmdline_session_setup(conn->session);//設定session中的最主要的幾個引數ip埠密碼等

如果spicy命令列沒設定引數,下面程式碼會去判斷session物件中到底有沒有沒初始化引數,沒有的話會呼叫一個介面,然後卡在那邊讓你寫引數,spicy_connect_dialog實現了一個gtk的介面。

    g_object_get(conn->session,
                 "unix-path", &unix_path,
                 "host", &host,
                 "port", &port,
                 "tls-port", &tls_port,
                 NULL);
    /* If user doesn't provide hostname and port, show the dialog window
       instead of connecting to server automatically */
    if ((host == NULL || (port == NULL && tls_port == NULL)) && unix_path == NULL) {
        if (!spicy_connect_dialog(conn->session)) {
            exit(0);
        }
    }

spicy不加引數呼叫spicy_connect_dialog,下面是介面。

spicy_connect_dialog介面

 

        點選connect會以後會設定引數之後spicy_connect_dialog正常返回,一切引數準備就緒以後,執行connection_connect(conn)連線遠端桌面。g_main_loop_run(mainloop);主程式處於睡眠狀態。

        connection_connect(conn);//連線遠端桌面
        if (connections > 0)
                g_main_loop_run(mainloop);
//主函式睡眠,直到呼叫connection_destroy中的g_main_loop_quit才會往下走
        g_main_loop_unref(mainloop);

這裡是spicy的大概的流程,關於connection_connect(conn);以後再分析。