转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>
在前段时间写的一篇BLOG中,我介绍了DirectFB同时显示到X11和VNC的方法。那是一个有趣的实验,为此我兴奋了好一会儿,不过没有什么太大的实用价值,因为broncho平台使用的GTK/DirectFB作为GUI,显示通过fbdev(framebuffer)输出到LCD。我们要做的是让DirectFB同时显示到fbdev和VNC上,这个功能作为手机探索者的一部分,现在是实现的时候了。这并不难,由于一些小问题,我还是花了一整天时间才搞定。
一、 下载libVNCServer,然后编译和安装。要确保通过环境变量PATH能找到vncserver的配置脚本。
二、 为DirectFB创建fbvnc的system模块。
1. 把fbdev拷贝为fbvnc,把所有的文件名、变量名和函数名,从fbdev改名为fbvnc。
2. 修改fbvnc.c,包含下列头文件。
- #include <direct/thread.h>
- #include <core/input.h>
- #include <rfb/rfb.h>
- #include <rfb/keysym.h
3. 修改fbvnc.c,声明下列函数和变量。
- static rfbScreenInfoPtr rfb_screen = NULL;
- static CoreInputDevice *vncPointerDevice = NULL;
- static CoreInputDevice *vncKeyboardDevice = NULL;
- static int g_vnc_client_nr = 0;
- typedef struct _ClientData
- {
- int oldButtonMask;
- int pressed;
- int oldx;
- int oldy;
- } ClientData;
- static void vnc_client_gone(rfbClientPtr cl);
- static enum rfbNewClientAction vnc_client_new(rfbClientPtr cl);
- static bool vnc_translate_key(rfbKeySym key, DFBInputEvent *evt);
- static void* vnc_server_thread( DirectThread *thread, void *data);
- static void* vnc_refresh_thread( DirectThread *thread, void *data);
- static void vnc_process_key_event(rfbBool down, rfbKeySym key, struct _rfbClientRec* cl);
- static void vnc_process_pointer_event(int buttonMask, int x, int y, struct _rfbClientRec* cl);
- static DFBResult vnc_update_screen(unsigned short* src, int x, int y, int w, int h );
- static DFBResult vnc_set_video_mode(DFBDisplayLayerConfig *config );
- static DFBEnumerationResult vnc_attach_keyboard_device( CoreInputDevice *device, void *ctx );
- static DFBEnumerationResult vnc_attach_pointer_device( CoreInputDevice *device, void *ctx );
4. 修改fbvnc.c,在primaryInitLayer中调用vnc_set_video_mode。
5. 修改fbvnc.c,实现下列函数。
- static DFBEnumerationResult
- vnc_attach_keyboard_device( CoreInputDevice *device,
- void *ctx )
- {
- vncKeyboardDevice = device;
- return DFENUM_OK;
- }
- static DFBEnumerationResult
- vnc_attach_pointer_device( CoreInputDevice *device,
- void *ctx )
- {
- vncPointerDevice = device;
- return DFENUM_OK;
- }
- static void vnc_client_gone(rfbClientPtr cl)
- {
- g_vnc_client_nr--;
- free(cl->clientData);
- return;
- }
- static enum rfbNewClientAction vnc_client_new(rfbClientPtr cl)
- {
- g_vnc_client_nr++;
- cl->clientData = (void*)calloc(sizeof(ClientData),1);
- cl->clientGoneHook = vnc_client_gone;
- return RFB_CLIENT_ACCEPT;
- }
- static void
- vnc_process_pointer_event(int buttonMask, int x, int y, rfbClientPtr cl)
- {
- DFBInputEvent evt = {0};
- int button = 0;
- if( vncPointerDevice == NULL ){
- /* Attach to first input device */
- dfb_input_enumerate_devices( vnc_attach_pointer_device,NULL,
- DICAPS_BUTTONS|DICAPS_AXES);
- D_ASSERT(vncPointerDevice);
- }
- ClientData* cd=cl->clientData;
- if(buttonMask != cd->oldButtonMask ) {
- int mask = buttonMask^cd->oldButtonMask;
- if( mask & (1 << 0)) {
- button=DIBI_LEFT;
- } else if( mask & (1 << 1)) {
- button=DIBI_MIDDLE;
- } else if( mask & (1 << 2)) {
- button=DIBI_RIGHT;
- } else {
- return;
- }
- evt.flags = DIEF_NONE;
- if(cd->pressed)
- {
- evt.type = DIET_BUTTONRELEASE;
- cd->pressed=0;
- cd->oldButtonMask = 0;
- }else {
- evt.type = DIET_BUTTONPRESS;
- cd->pressed=1;
- cd->oldButtonMask=buttonMask;
- }
- evt.button=button;
- printf("%s %d %d %d %d/n", __func__, button, cd->pressed, x, y);
- dfb_input_dispatch( vncPointerDevice, &evt );
- cd->oldx=x;
- cd->oldy=y;
- return;
- }
- evt.type = DIET_AXISMOTION;
- evt.flags = DIEF_AXISABS;
- if( cd->oldx != x ) {
- evt.axis = DIAI_X;
- evt.axisabs = x;
- dfb_input_dispatch( vncPointerDevice, &evt );
- }
- if( cd->oldy != y ) {
- evt.axis = DIAI_Y;
- evt.axisabs = y;
- dfb_input_dispatch( vncPointerDevice, &evt );
- }
- cd->oldx=x;
- cd->oldy=y;
- dfb_input_dispatch( vncPointerDevice, &evt );
- rfbDefaultPtrAddEvent(buttonMask,x,y,cl);
- }
- /*
- * declaration of private data
- */
- static void
- vnc_process_key_event(rfbBool down, rfbKeySym key, rfbClientPtr cl)
- {
- DFBInputEvent evt;
- if( vncKeyboardDevice == NULL ){
- /* Attach to first input device */
- dfb_input_enumerate_devices( vnc_attach_keyboard_device,NULL, DICAPS_KEYS);
- D_ASSERT(vncKeyboardDevice);
- }
- if (down)
- evt.type = DIET_KEYPRESS;
- else
- evt.type = DIET_KEYRELEASE;
- if (vnc_translate_key( key, &evt )) {
- dfb_input_dispatch( vncKeyboardDevice, &evt );
- }
- }
- static bool
- vnc_translate_key(rfbKeySym key, DFBInputEvent *evt )
- {
- /* Unicode */
- if (key <= 0xf000) {
- evt->flags = DIEF_KEYSYMBOL;
- evt->key_symbol = key;
- return true;
- }
- /* Dead keys */
- /* todo */
- /* Numeric keypad */
- if (key >= XK_KP_0 && key <= XK_KP_9) {
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_0 + key - XK_KP_0;
- return true;
- }
- /* Function keys */
- if (key >= XK_F1 && key <= XK_F11) {
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_F1 + key - XK_F1;
- return true;
- }
- switch (key) {
- /* Numeric keypad */
- case XK_KP_Decimal:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_DECIMAL;
- break;
- case XK_KP_Separator:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_SEPARATOR;
- break;
- case XK_KP_Divide:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_DIV;
- break;
- case XK_KP_Multiply:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_MULT;
- break;
- case XK_KP_Subtract:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_MINUS;
- break;
- case XK_KP_Add:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_PLUS;
- break;
- case XK_KP_Enter:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_ENTER;
- break;
- case XK_KP_Equal:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_KP_EQUAL;
- break;
- /* Arrows + Home/End pad */
- case XK_Up:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_UP;
- break;
- case XK_Down:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_DOWN;
- break;
- case XK_Right:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_RIGHT;
- break;
- case XK_Left:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_LEFT;
- break;
- case XK_Insert:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_INSERT;
- break;
- case XK_Delete:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_DELETE;
- break;
- case XK_Home:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_HOME;
- break;
- case XK_End:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_END;
- break;
- case XK_Page_Up:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_PAGE_UP;
- break;
- case XK_Page_Down:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_PAGE_DOWN;
- break;
- /* Key state modifier keys */
- case XK_Num_Lock:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_NUM_LOCK;
- break;
- case XK_Caps_Lock:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_CAPS_LOCK;
- break;
- case XK_Scroll_Lock:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_SCROLL_LOCK;
- break;
- case XK_Shift_R:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_SHIFT_R;
- break;
- case XK_Shift_L:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_SHIFT_L;
- break;
- case XK_Control_R:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_CONTROL_R;
- break;
- case XK_Control_L:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_CONTROL_L;
- break;
- case XK_Alt_R:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_ALT_R;
- break;
- case XK_Alt_L:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_ALT_L;
- break;
- case XK_Meta_R:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_META_R;
- break;
- case XK_Meta_L:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_META_L;
- break;
- case XK_Super_L:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_SUPER_L;
- break;
- case XK_Super_R:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_SUPER_R;
- break;
- case XK_Hyper_L:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_HYPER_L;
- break;
- case XK_Hyper_R:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_HYPER_R;
- break;
- /*case ??:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_ALTGR;
- break;*/
- case XK_BackSpace:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_BACKSPACE;
- break;
- case XK_Tab:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_HYPER_L;
- break;
- case XK_Return:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_ENTER;
- break;
- case XK_Escape:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_ESCAPE;
- break;
- case XK_Pause:
- evt->flags = DIEF_KEYID;
- evt->key_id = DIKI_PAUSE;
- break;
- /* Miscellaneous function keys */
- case XK_Help:
- evt->flags = DIEF_KEYSYMBOL;
- evt->key_symbol = DIKS_HELP;
- break;
- case XK_Print:
- evt->flags = DIEF_KEYSYMBOL;
- evt->key_symbol = DIKS_PRINT;
- break;
- case XK_Break:
- evt->flags = DIEF_KEYSYMBOL;
- evt->key_symbol = DIKS_BREAK;
- break;
- default:
- return false;
- }
- return true;
- }
- #define RGB16_TO_RGB32(pixel) ( (((pixel) & 0xF800) << 8) | /
- (((pixel) & 0x07E0) << 5) | /
- (((pixel) & 0x001F) << 3) )
- static DFBResult
- vnc_update_screen(unsigned short* src, int x, int y, int w, int h )
- {
- int i = 0;
- int j = 0;
- D_ASSERT( rfb_screen != NULL );
- D_ASSERT( rfb_screen->frameBuffer != NULL );
- if(g_vnc_client_nr <= 0
- || src == NULL
- || rfb_screen == NULL
- || rfb_screen->frameBuffer == NULL)
- {
- return DFB_FALSE;
- }
- int d_bpp = 4;
- char* dst = rfb_screen->frameBuffer;
- char* src_line = src;
- char* src_row = src;
- char* dst_line = dst;
- char* dst_row = dst;
- struct fb_fix_screeninfo* fix = &(dfb_fbvnc->shared->fix);
- struct fb_var_screeninfo* var = &(dfb_fbvnc->shared->current_var);
- int s_bpp = (var->bits_per_pixel + 7) / 8;
- if(w > var->xres_virtual
- || h > var->yres_virtual)
- {
- return DFB_FALSE;
- }
- for (i = y; i < h; i++)
- {
- src_row = src_line;
- dst_row = dst_line;
- for(j = x; j < w; j++)
- {
- src_row += s_bpp;
- dst_row += d_bpp;
- *(int*)dst_row = RGB16_TO_RGB32(*(short*)src_row);
- }
- src_line += fix->line_length;
- dst_line = dst_row;
- }
- rfbMarkRectAsModified ( rfb_screen, x, y, x+w, y+h );
- return DFB_OK;
- }
- DFBResult
- vnc_set_video_mode(DFBDisplayLayerConfig *config )
- {
- int argc = 0;
- char** argv = NULL;
- struct fb_var_screeninfo* var = &(dfb_fbvnc->shared->current_var);
- int height = var->yres_virtual;
- int width = var->xres_virtual;
- D_DEBUG( "DirectFB/VNC: layer config properties/n");
- if(rfb_screen) return DFB_OK;
- rfb_screen = rfbGetScreen(&argc, argv, width, height, 8, 3, 4);
- if ( rfb_screen == NULL )
- {
- D_ERROR( "DirectFB/VNC: Couldn't set %dx%dx%d video mode/n",
- config->width, config->height,
- config->pixelformat);
- return DFB_FAILURE;
- }
- rfb_screen->frameBuffer = malloc(width*height*rfb_screen->depth/8) ;
- rfb_screen->kbdAddEvent = vnc_process_key_event;
- rfb_screen->ptrAddEvent = vnc_process_pointer_event;
- rfb_screen->newClientHook = vnc_client_new;
- rfbInitServer(rfb_screen);
- direct_thread_create( DTT_OUTPUT, vnc_server_thread, rfb_screen, "VNC Output" );
- direct_thread_create( DTT_OUTPUT, vnc_refresh_thread, rfb_screen, "VNC Refresh" );
- return DFB_OK;
- }
- static void*
- vnc_server_thread( DirectThread *thread, void *data )
- {
- rfbRunEventLoop(rfb_screen, -1, FALSE);
- return NULL;
- }
- static void*
- vnc_refresh_thread( DirectThread *thread, void *data )
- {
- while(1)
- {
- if(dfb_fbvnc != NULL && dfb_fbvnc->framebuffer_base != NULL && g_vnc_client_nr > 0)
- {
- vnc_update_screen(dfb_fbvnc->framebuffer_base, 0, 0,
- rfb_screen->width, rfb_screen->height);
- }
- usleep(200000);
- }
- return NULL;
- }
三、 修改directfbrc,让system=fbvnc。
一切OK了(颜色还有点问题),在broncho上的效果图: