• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 16, 文章 - 0, 評論 - 55, 引用 - 0
            數(shù)據(jù)加載中……

            fltk剖析 main-loop(二)

            做為一個以c++為目標語言且要適配各種平臺的界面庫,F(xiàn)LTK注定是小眾的,所以寫的內(nèi)容要限定一下受眾。如果你對c/c++比較熟悉,至少對某一種操作系統(tǒng)的API比較熟悉,希望找到某種一次編寫到處編譯的界面庫,同時對靈活性和尺寸比較在意,那么這個文檔就比較適合你。如果你只是希望學會怎么使用fltk,并不想深入了解它背后的原理,那么這個文檔就不太適合,fltk的在線文檔在這里:http://www.fltk.org/documentation.php

            fltk最初的思路來自于1987年的NeXT系統(tǒng),初始版本針對的是X,所以代碼里有一些用X開頭的函數(shù)名,但隨著代碼的不斷演進,接口逐漸變得和系統(tǒng)無關。

            基本上,fltk認為所有的操作系統(tǒng)都會提供以下幾種功能:
            1.窗口創(chuàng)建和銷毀
            2.繪圖(點,直線,曲線,圓...)
            3.字體顯示
            4.輸入設備交互(鍵盤、鼠標)

            只要有這幾種功能,不需要系統(tǒng)提供全套的控件,也可以自行構建出界面。另外系統(tǒng)還會提供一些附加功能,對于豐富界面也很有幫助,但并不是充分必要條件,比如
            1.圖片讀寫
            2.文件操作
            3.打印機
            4.輸入法

            基于這樣的認知,做為一個GUI庫,fltk需要提供一個模型,把這些元素組合在一起,既要有足夠的彈性又要足夠簡單,F(xiàn)LTK采用的是main-loop,相信很多人開始學習c語言的時候都會寫下面的代碼:
            #include <stdio.h>
            int main(int argc, char** argv)
            {
               printf("hello world\n");
               return 0;
            }

            fltk所使用的模型就和這個類似,用偽代碼表示就是:
            #include <fltk.h>
            int main(int argc, char **argv)
            {
               create_window(); // 創(chuàng)建窗口
               create_widget(); // 創(chuàng)建控件
               while (1) {
                  if ( wait() ) break; // 事件循環(huán)
               }
               return 0;
            }

            是不是和gtk很類似?

            這個模型的好處是容易理解,如果把所有的流程都用class包裹起來,雖然貌似充滿了oo的味道,但是對于理解代碼反而是有害的。任何代碼都有一個入口,為了面向對象,甚至把入口也藏起來,只會增加學習者的困擾。比如mfc,qt,juce,wxwidgets,如果想分析代碼,光是找到起點就很不容易,尤其為了oo,很多GUI庫用宏將main都包裹了起來,更增加了理解的難度。代碼不應該讓編譯器舒服,也不應該屈從于某種思想,而是應該以人為本,讓程序員看的輕松用的輕松。人的注意力是有限的,短期記憶大概只有十幾分鐘的時間,同時注意到的目標也不多,而且似乎人的思維模式是線性的,也就是說只能在一條線上做深入思考,并行處理好幾個問題,大腦會短路。當然有些發(fā)達的大腦有一心多用的本領,但是總要照顧大多數(shù)人吧?

            首先談談這個main(),為什么叫這個名字?這和編譯器和操作系統(tǒng)有關,具體原因可以自行百度,重要的只有一條,這是程序的入口。事實上并不是所有的操作系統(tǒng)都用這個名稱,osx/ios/linux是用main,windows/wince用的是winmain,android/windows phone干脆沒有main,所以要為所有的平臺編寫統(tǒng)一的main。先看看windows平臺的實現(xiàn),打開fltk的源代碼,找到src/fl_call_main.c
            extern int main(int, char *[]);
            int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
            {
              int rc, i;
              char **ar;

            #  ifdef _DEBUG
            // 這里用來創(chuàng)建一個cmd窗口,或者叫dos窗口,用以輸出調試結果,只在debug版里提供
             /*
              * If we are using compiling in debug mode, open a console window so
              * we can see any printf's, etc...
              *
              * While we can detect if the program was run from the command-line -
              * look at the CMDLINE environment variable, it will be "WIN" for
              * programs started from the GUI - the shell seems to run all WIN32
              * applications in the background anyways...
              */

              AllocConsole();
              freopen("conin$", "r", stdin);
              freopen("conout$", "w", stdout);
              freopen("conout$", "w", stderr);
            #  endif /* _DEBUG */

              ar = (char**) malloc(sizeof(char*) * (__argc + 1));
              i = 0;
              while (i < __argc) {
                int l;
                unsigned dstlen;
                if (__wargv ) {
                  for (l = 0; __wargv[i] && __wargv[i][l]; l++) {}; /* is this just wstrlen??? */
                  dstlen = (l * 5) + 1;
                  ar[i] = (char*) malloc(dstlen);
            /*    ar[i][fl_unicode2utf(__wargv[i], l, ar[i])] = 0; */
                  dstlen = fl_utf8fromwc(ar[i], dstlen, __wargv[i], l);
                  ar[i][dstlen] = 0;
                } else {
                  for (l = 0; __argv[i] && __argv[i][l]; l++) {};
                  dstlen = (l * 5) + 1;
                  ar[i] = (char*) malloc(dstlen);
            /*      ar[i][mbcs2utf(__argv[i], l, ar[i], dstlen)] = 0; */
                  ar[i][mbcs2utf(__argv[i], l, ar[i])] = 0;
                }
                i++;
              }
              ar[__argc] = 0;
              /* Run the standard main entry point function... */
              rc = main(__argc, ar);

            #  ifdef _DEBUG
              fclose(stdin);
              fclose(stdout);
              fclose(stderr);
            #  endif /* _DEBUG */

              return rc;
            }

            看起來很簡單,就是將winmain包裝了一下,做了一些初始化的工作,再引出main。

            osx/linux直接使用了main,所以沒什么可解釋的

            接下來是loop。在windows下面比較好理解,打開src/Fl_win32.cxx,找到如下的代碼:
            int fl_wait(double time_to_wait) {
              ...
              if (Fl::idle && !in_idle) { // 若處于空閑時間且存在idle函數(shù),執(zhí)行之
                in_idle = 1;
                Fl::idle();
                in_idle = 0;
              }
              ... 
              while ((have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE)) > 0) {
                if (fl_send_system_handlers(&fl_msg))
                  continue;

                // Let applications treat WM_QUIT identical to SIGTERM on *nix
                if (fl_msg.message == WM_QUIT)
                  raise(SIGTERM);

                if (fl_msg.message == fl_wake_msg) {
                  // Used for awaking wait() from another thread
                  thread_message_ = (void*)fl_msg.wParam;
                  process_awake_handler_requests();
                }

                TranslateMessage(&fl_msg);
                DispatchMessageW(&fl_msg);
              }
              ...
              return 1;
            }
            基本上就是<<Windows程序設計>>上的那一套,就不做說明了

            再打開src/Fl_x.cxx,找到fl_wait函數(shù),這里是linux下的loop主體,具體代碼就不分析了,有興趣的可以去找X編程的資料

            最后是osx的loop,在osx下面runlooper是不能由程序直接控制的,只能通過外圍發(fā)送和接收消息的方式曲線救國,所以FLTK用了一個線程,然后在線程里和runlooper交互。打開src/Fl_cocoa.mm,找到fl_wait函數(shù),再找到DataReady類,這兩個部分組合起來就構成了osx的loop功能,具體實現(xiàn)是用object-c和c/c++混合完成的

            以上是各個系統(tǒng)各自的loop功能,最后還要將他們整合起來,打開src/Fl.cxx:
            int Fl::run() {
              while (Fl_X::first) wait(FOREVER);
              return 0;
            }

            double Fl::wait(double time_to_wait) {
              // delete all widgets that were listed during callbacks
              do_widget_deletion();

            #ifdef WIN32

              return fl_wait(time_to_wait);

            #elif defined(__APPLE__)

              run_checks();
              return fl_mac_flush_and_wait(time_to_wait);

            #else

              if (first_timeout) {
                elapse_timeouts();
                Timeout *t;
                while ((t = first_timeout)) {
                  if (t->time > 0) break;
                  // The first timeout in the array has expired.
                  missed_timeout_by = t->time;
                  // We must remove timeout from array before doing the callback:
                  void (*cb)(void*) = t->cb;
                  void *argp = t->arg;
                  first_timeout = t->next;
                  t->next = free_timeout;
                  free_timeout = t;
                  // Now it is safe for the callback to do add_timeout:
                  cb(argp);
                }
              } else {
                reset_clock = 1; // we are not going to check the clock
              }
              run_checks();
            //  if (idle && !fl_ready()) {
              if (idle) {
                if (!in_idle) {
                  in_idle = 1;
                  idle();
                  in_idle = 0;
                }
                // the idle function may turn off idle, we can then wait:
                if (idle) time_to_wait = 0.0;
              }
              if (first_timeout && first_timeout->time < time_to_wait)
                time_to_wait = first_timeout->time;
              if (time_to_wait <= 0.0) {
                // do flush second so that the results of events are visible:
                int ret = fl_wait(0.0);
                flush();
                return ret;
              } else {
                // do flush first so that user sees the display:
                flush();
                if (idle && !in_idle) // 'idle' may have been set within flush()
                  time_to_wait = 0.0;
                return fl_wait(time_to_wait);
              }
            #endif
            }

            看起來很明顯,就是將各個平臺的fl_wait包裝起來組合成統(tǒng)一的接口,現(xiàn)在看一個fltk的示例代碼:test/hello.cxx
            #include <FL/Fl.H>
            #include <FL/Fl_Window.H>
            #include <FL/Fl_Box.H>

            int main(int argc, char **argv) {
              Fl_Window *window = new Fl_Window(340,180);
              Fl_Box *box = new Fl_Box(20,40,300,100,"Hello, World!");
              box->box(FL_UP_BOX);
              box->labelfont(FL_BOLD+FL_ITALIC);
              box->labelsize(36);
              box->labeltype(FL_SHADOW_LABEL);
              window->end();
              window->show(argc, argv);
              return Fl::run();
            }

            將Fl::run()展開,就是
            int main(int argc, char **argv) {
              .. // create windows and widgets
             
              while (Fl_X::first) wait(FOREVER);
              return 0;
            }

            這就是FLTK的main-loop模型。簡單,實用,好理解

            posted on 2015-11-01 11:58 cyantree 閱讀(2228) 評論(0)  編輯 收藏 引用

            久久精品国产只有精品2020| 亚洲精品tv久久久久久久久| 亚洲成人精品久久| 久久久精品国产亚洲成人满18免费网站 | 中文字幕乱码久久午夜| 久久久久久亚洲AV无码专区| 久久国产乱子伦免费精品| 伊人久久综合热线大杳蕉下载| 一本久久a久久精品综合香蕉| 久久久久女人精品毛片| 久久九九久精品国产免费直播| 精品一二三区久久aaa片| 99久久婷婷国产一区二区| 久久午夜免费视频| 狠狠色综合网站久久久久久久 | 国产成人无码精品久久久性色| 蜜臀av性久久久久蜜臀aⅴ| 久久久久无码中| 精品免费tv久久久久久久| 久久精品成人欧美大片| 久久婷婷人人澡人人| 国内精品久久久久影院免费| 伊人久久久AV老熟妇色| 亚洲Av无码国产情品久久| 国产激情久久久久影院老熟女| 麻豆一区二区99久久久久| 亚洲人成无码www久久久| 久久国产免费直播| 国产91久久综合| 99久久精品国产毛片| 天天爽天天爽天天片a久久网| 久久综合给久久狠狠97色| 亚洲va中文字幕无码久久| 久久久国产精华液| 精品国产青草久久久久福利| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 久久久精品国产sm调教网站| 777午夜精品久久av蜜臀| 亚洲va久久久噜噜噜久久狠狠| 日韩人妻无码一区二区三区久久 | 国产精品9999久久久久|