minigui源码学习

2 篇文章 0 订阅

前言:

个人习惯学习源码先从编译结构了解代码模块,所以先从编译结构开始了解。
如果编译角度搞不定,就代码开始运行时,各个模块初始化的角度开始了解。

学习源码——编译角度

下载源码

    git clone https://github.com/VincentWei/minigui.gitminigui

编译

minigui使用autotools工具来生成Makefile文件,然后再执行make编译。

一般操作流程是:

autoconf
auto make
./configure
make

但是不起作用啊,autoconf执行失败。

于是直接运行autogen.sh,然后autoconf,automake,./configure都可运行了,但是make运行失败。

想了想,还是放弃从编译角度切入了吧

  1. 自动生成的Makefile太过复杂,难以解析
  2. autotools工具也不熟

学习源码——运行角度

初始化入口:

MiniGUIAppMain

MiniGUIAppMain是用例的入口函数,所以从这个函数开始顺腾摸瓜,了解代码的模块和结构
MiniGUIAppMain的定义:

                        #define MiniGUIMain \
                        MiniGUIAppMain (int args, const char* argv[]); \
                        int main_entry (int args, const char* argv[]) \
                        { \
                        int iRet = 0; \
                        if (InitGUI (args, argv) != 0) { \
                                return 1; \
                        } \
                        iRet = MiniGUIAppMain (args, argv); \
                        TerminateGUI (iRet); \
                        return iRet; \
                        } \
                        int MiniGUIAppMain

从代码可知,MiniGUIMain 会先运行InitGUI(),然后再运行MiniGUIAppMain,界面完了以后,还会调用TerminateGUI回收界面资源

模块初始化

InitGUI()总共调用了下面20多个初始化函数,前置工作准备

初始化各个模块初始化的函数,以及模块的流程如下:

                1. _sysvipc_mutex_sem_init
                2. mg_InitSliceAllocator
                3. mg_InitFixStr
                4. mg_InitMisc
                5. mg_InitGAL
                6. InstallSEGVHandle
                7. mg_InitSystemRes
                8. mg_InitGDI
                9. mg_InitScreenDC
                10. license_create
                11. mg_InitCursor
                12. mg_InitLWEvent
                13. mg_InitLFManager
                14. mg_InitMenu
                15. mg_InitControlClass
                16. mg_InitAccel
                17. mg_InitDesktop
                18. mg_InitFreeQMSGList
                19. createThreadInfoKey
                20. SystemThreads
                21. SetKeyboardLayout
                22. SetCursor
                23. SetCursorPos
                24. mg_TerminateMgEtc

模块作用了解

一般来说,每个模块会有个小的说明文档,但是没有找到,所以算了。
看源码应该大致能了解其作用,如果不能了解其作用,则跟着用例的创建窗口的代码往下跑,看模块是怎么用的来学习他吧。

模块作用总结如下:

1. _sysvipc_mutex_sem_init
   信号量初始化,信号量是跨进程的,用于进程间共享内存锁。

   这个函数内部清空了许多sem锁,然后重新创建。

   功能:初始化锁

2. mg_InitSliceAllocator

        作用尚未了结清楚,初步理解为窗口内存碎片整理

3. mg_InitFixStr

        功能:字串补全,内存占用2^n~2^(n+1)时,空间大小补全为2^(n+1)

4. mg_InitMisc

        功能:零零散散的杂项功能初始化,观其实现时是存取配置项功能。还有个clipboard剪辑版,暂时不了解

5. mg_InitGAL

        功能:创建一个video设备,并创建一个surface对象与video设备互相指向。video设备初始化时,会从底层map一段内存出来,然后由surface对其进行管理。
        读写surface时会直接改变界面。

6. InstallSEGVHandle

        功能:linux 系统信号监听及处理

7. InitSystemRes

        功能:建一张存数据的hash表,用于存储资源。添加bmp内部资源,添加icon资源,添加样式资源    

8. InitGDI

        全称:global graphic interface

        功能:包括了所有图形操作的接口

9. InitScreenDC

        功能:

                1. 初始化InitBlockDataHeap(__mg_FreeClipRectList)

                2. dc_InitClipRgnInfo,初始化dc池,后续取hdc就从这里取

                3. 初始化全局dc,__mg_screen_dc

                4. 初始化全局dc,__mg_screen_sys_dc

10. license_create

        功能:加载license相关资源,具体作用未明

11. InitCursor

        功能:初始化鼠标资源

12. mg_InitLWEvent

        功能:初始化IAL,事件输入模块

13. mg_InitLFManager

        功能:初始化系统默认样式

14. mg_InitMenu

        功能:菜单相关
                1. 初始化InitBlockDataHeap(MBHeap),作用未知
                2. 初始化InitBlockDataHeap(MIHeap),作用未知
                3. 初始化InitBlockDataHeap(TMIHeap),作用未知

15. mg_InitControlClass

        功能:注册系统默认样式的控件

16. mg_InitAccel

        功能:
                1. 初始化InitBlockDataHeap(ACHeap),作用未知
                2. 初始化InitBlockDataHeap(AIHeap),作用未知

17. mg_InitDesktop

        功能:

        1. 初始化z序的数据结构

        2. 初始化InitBlockDataHeap(sg_FreeClipRectList)

        3. 初始化InitBlockDataHeap(sg_FreeInvRectList)

        4. 初始化__mg_hwnd_desktop,也就是DESKTOP_HWND

        5. 初始化桌面的消息队列,__mg_hwnd_desktop->pMessages =  __mg_dsk_msg_queue

        6. 设置系统默认的控件样式

18. mg_InitFreeQMSGList

        功能:初始化InitBlockDataHeap(QMSGHeap),作用未知

19. createThreadInfoKey

        功能:初始化主线程的pthread_key,使主线程也有对应的线程标志。
                后续创建主窗口时,也是根据该线程标志来获取消息队列的,也许还有很多和主线程pthread_key相关的模块

20. SystemThreads

        功能:创建两个线程,一个是DesktopMain,另一个是EventLoop
                DesktopMain:不停的接收并处理消息,凡是发给桌面的消息,都在该线程处理
                EventLoop:运行IAL模块,不停地获取事件

21. SetKeyboardLayout

        功能:初始化键盘布局相关,作用未知

22. SetCursor&SetCursorPos

        功能:预测设置光标,作用未知

23. mg_TerminateMgEtc

        功能:结束配置管理模块,作用未知

GAL模块解析

按照从上向下的顺序,第一我先详细说明GAL模块。

GAL模块的初始化,我就不按初始化的流程来说明,我先将模块拆解成一个个小模块,然后介绍完小模块以后,再说明整个初始化的流程。

video驱动管理模块——video-drivers

video会有各种不同厂商的设备,不同厂商的设备会有不同的驱动,所以video-drivers模块包含了很多video视频设备的驱动

所有的这些video驱动模块都保存在boot_strap(VideoBootStrap**)这个全局变量当中

src/newgal/video.c 中可以看到这个boot_strap这个全局变量包含了哪些驱动:

/* Available video drivers */
static VideoBootStrap *bootstrap[] = {
#ifdef _MGGAL_DUMMY
&DUMMY_bootstrap,
#endif
#ifdef _MGGAL_FBCON
&FBCON_bootstrap,
#endif
#ifdef _MGGAL_QVFB
&QVFB_bootstrap,
#endif
...
NULL
};

然后我们只挑FBCON_bootstrap,稍微深入了解下。

fbcon驱动模块

fbcon模块的源码目录在:src\newgal\fbcon\

我们先看fbvideo.c文件,FBCON_bootstrap的定义:

                VideoBootStrap FBCON_bootstrap = {
                "fbcon", "Linux Framebuffer Console",
                FB_Available, FB_CreateDevice
                };

GAL模块初始化,选中FBCON_bootstrap驱动时,会调用FB_CreateDevice获取一个video设备对象,我们称之为’video’

以后我们看看FB_CreateDevice,对返回的video设备对象做了哪些初始化:

                this->VideoInit = FB_VideoInit;
                this->ListModes = FB_ListModes;
                this->SetVideoMode = FB_SetVideoMode;
                this->SetColors = FB_SetColors;
                this->VideoQuit = FB_VideoQuit;
                this->AllocHWSurface = FB_AllocHWSurface;
                this->CheckHWBlit = NULL;
                this->FillHWRect = NULL;
                this->SetHWColorKey = NULL;
                this->SetHWAlpha = NULL;
                this->UpdateRects = NULL;
                this->FreeHWSurface = FB_FreeHWSurface;
                this->GetFBInfo = FB_GetFBInfo;
                this->free = FB_DeleteDevice;

这些函数就是video设备对象可以执行的操作了
操作很多也很复杂,但是我只了解了FB_VideoInit和FB_SetVideoMode两个函数的实现
因为初始化就用到了这两个函数

FB_VideoInit

FB_VideoInit函数中,映射了一段内存到video->hidden->mapped_mem:

#define mapped_mem          (this->hidden->mapped_mem)
mapped_mem = mmap(NULL, mapped_memlen,
        PROT_READ|PROT_WRITE, MAP_SHARED, console_fd, 0);

以及计算了一个偏移地址:

ioctl(console_fd, FBIOGET_FSCREENINFO, &finfo)
/* Memory map the device, compensating for buggy PPC mmap() */
mapped_offset = (((long)finfo.smem_start) -
                (((long)finfo.smem_start)&~(getpagesize () - 1)));
mapped_memlen = finfo.smem_len+mapped_offset;
FB_SetVideoMode

FB_SetVideoMode函数中,对传入的参数current(也就是surface)进行了初始化

/* Set up the new mode framebuffer */
current->flags = (GAL_FULLSCREEN|GAL_HWSURFACE);
current->w = vinfo.xres;
current->h = vinfo.yres;
current->pitch = finfo.line_length;
current->pixels = mapped_mem+mapped_offset;

其实SetVideoMode就有点像告诉video驱动,我要要显示的图像的大小,格式,你给我一张合适的画布放surface里,我自己绘图

总结:映射的这段buffer,会被video控制器以一定频率不停的读取显示到video设备上
而这段内存又保存在了surface中,所以操作surface->pixels,就有点像直接在设备界面上绘图

surface模块

源码位置:src/newgal/surface.c
surface模块的初始化入口是GAL_CreateRGBSurface函数,这个函数里面会new一个surface对象,
然后由GAL_VideoSurface这个全局指针接管。据我目前观察,之后大多数情况都是使用的GAL_VideoSurface

GAL初始化流程

文件入口:src\newgal\newgal.c

mg_InitGAL:
        GAL_VideoInit:
                GAL_GetVideo:
                        FBCON_bootstrap->create:
                                FB_CreateDevice:
                                        创建一个video设备对象,并返回
                video->VideoInit:
                        映射一段buffer保存在video->hidden->mapped_mem中
                
                初始化全局变量:
                        current_video = video
                        current_video->screen = NULL

                GAL_VideoSurface = GAL_CreateRGBSurface(...):
                        创建一个surface
                
                初始化全局变量:
                        GAL_VideoSurface->video = current_video;

        __gal_screen = GAL_SetVideoMode:
                video->SetVideoMode:
                        根据mode设置surface内容,并返回
        初始化GDI模块的全局变量:SysPixelIndex

IAL模块解析

函数入口:mg_InitLWEvent->mg_InitIAL
文件入口:src/kernel/event.c->src/ial/ial.c

负责事件获取,并转成windows message上传到设备端

IAL引擎

同video有众多设备驱动一样,IAL也有众多引擎
根据配置里的IAL引擎名称,IAL模块会选择其中一个引擎赋值到全局变量__mg_cur_input中
然后使用__mg_cur_input来进行引擎操作,一下是IAL所有的引擎,我只取libinput说明一下

static INPUT inputs [] =
{
/* General IAL engines ... */
#ifdef _MGIAL_QVFB
{"qvfb", InitQVFBInput, TermQVFBInput},
#endif
...
#ifdef _MGIAL_LIBINPUT
{"libinput", InitLibInput, TermLibInput},
#endif
/* ... end of general IAL engines */
};
libinput

libinput的入口函数是InitLibInput,IAL获取到libinput之后,便会调用该接口
这里我只说明两个函数,一个是初始化函数InitLibInput,以及wait_event_ex
因为初始化会用到InitLibInput,以及之后读取事件会调用wait_event_ex

InitLibInput

InitLibInput对于input引擎的初始化:

...
input->update_mouse = mouse_update;
input->get_mouse_xy = mouse_getxy;
input->set_mouse_xy = mouse_setxy;
input->get_mouse_button = mouse_getbutton;
input->set_mouse_range = mouse_setrange;
input->suspend_mouse= input_suspend;
input->resume_mouse = input_resume;

input->update_keyboard = keyboard_update;
input->get_keyboard_state = keyboard_getstate;
input->suspend_keyboard = input_suspend;
input->resume_keyboard = input_resume;
input->set_leds = set_leds;

input->wait_event_ex = wait_event_ex;
...
wait_event_ex
wait_event_ex可以获取事件,并返回事件的类型

IAL初始化流程

mg_InitIAL:
InitLibInput:
初始化__mg_cur_input,并给其接口赋初值,之后便是用这个全局对象获取事件

SystemThreads模块解析

函数入口:SystemThreads
文件入口:src/kernel/init.c

该模块主要是运行起桌面模块和IAL模块,将这两者关联到一块

创建线程DesktopMain

初始化桌面

调用init_desktop_win函数,初始化桌面参数,如下:

...
static MAINWIN desktop_win;
pDesktopWin = &desktop_win;
pDesktopWin->pMessages         = __mg_dsk_msg_queue;
pDesktopWin->MainWindowProc    = DesktopWinProc;
...
pDesktopWin->pMainWin          = pDesktopWin;
pDesktopWin->we_rdr            = __mg_def_renderer;

__mg_hwnd_desktop = (HWND)pDesktopWin;
__mg_dsk_win  = pDesktopWin;
...

注意:

#define HWND_DESKTOP        __mg_hwnd_desktop

也就是说HWND_DESKTOP就是桌面本身,类型是MAINWIN

消息处理

获取并处理消息的步骤如下:

while (GetMessage(&Msg, HWND_DESKTOP)) {
...
DesktopWinProc (HWND_DESKTOP, 
Msg.message, Msg.wParam, Msg.lParam);
...
}

创建线程EventLoop

循环读消息
IAL_WaitEvent
发消息
发送消息到HWND_DESKTOP桌面的毁掉函数中

control模块解析

函数入口:mg_InitControlClass
文件入口:src\gui\ctrlclass.c

button控件

文件入口:src\control\button.c
函数入口:RegisterButtonControl

控件有很多自定义模块,我这里就专门挑button说下
控件的绘制是在WinProc中的WM_PAINT中进行的,RegisterButtonControl函数中会
将WNDCLASS类型的对象填上一些参数,然后调用AddNewControlClass。具体代码如下:

BOOL RegisterButtonControl (void)
{
WNDCLASS WndClass;

WndClass.spClassName = CTRL_BUTTON;
WndClass.dwStyle     = WS_NONE;
WndClass.dwExStyle   = WS_EX_NONE;
WndClass.hCursor     = GetSystemCursor (IDC_ARROW);
WndClass.iBkColor    = 
GetWindowElementPixel (HWND_NULL, WE_MAINC_THREED_BODY);
WndClass.WinProc     = ButtonCtrlProc;

return AddNewControlClass (&WndClass) == ERR_OK;
}

AddNewControlClass函数会根据WndClass的内容创建一个CTRLCLASSINFO结构的对象,然后将其保存到ccitable链表中

初始化流程

BOOL mg_InitControlClass ()
{
int i;

for (i=0; i<LEN_CCITABLE; i++)
ccitable[i] = NULL;

// Register system controls here.
#ifdef _MGCTRL_STATIC
if (!RegisterStaticControl ())
return FALSE;
#endif

#ifdef _MGCTRL_BUTTON
if (!RegisterButtonControl())
return FALSE;
#endif
...
}

GDI模块初始化

入口函数有两个:mg_InitScreenDC,mg_InitGDI
入口文件:src/newgdi/gdi.c

前面模块了解部分已经初步学习,所以就不写了

mg_InitScreenDC

dc_InitClipRgnInfo

初始化DC池,将每一个DC和surface挂钩,具体初始化如下:

for (i=0; i<DCSLOTNUMBER; i++) {
/* Local clip region */
InitClipRgn (&DCSlot[i].lcrgn, &__mg_FreeClipRectList);
MAKE_REGION_INFINITE(&DCSlot[i].lcrgn);

/* Global clip region info */
DCSlot[i].pGCRInfo = NULL;
DCSlot[i].oldage = 0;

/* Effective clip region-- 有效剪辑区,暂时不了解 */
InitClipRgn (&DCSlot[i].ecrgn, &__mg_FreeClipRectList);
}
dc_InitScreenDC

调用两次,初始化__mg_screen_dc和__mg_screen_sys_dc
具体初始化的内容,我提炼了一下,如下:

1. pdc->DataType = TYPE_HDC;
2. 初始化bkcolor
3. 初始化画笔颜色,和样式
4. 初始化画刷颜色
5. 初始化DevRC,设备可显示屏幕大小
6. pdc->surface = surface;
7. pdc->cur_dst = pdc->surface->pixels;
...
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值