您的位置:首页 > 其它

手机探索者开发实录—Broncho支持VNC

2008-09-21 19:03 183 查看
手机探索者开发实录—Broncho支持VNC

转载时请注明出处和作者联系方式
作者联系方式:李先静 <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上的效果图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: