wf_post_connect中的gdi_init
这个函数只要作用是创建primary,并且设置gdi回调,比如,直线用哪个函数画,曲线用哪个函数。如果直接使用gdi_init,则gdi->primary_buffer就是绘制产生的图像rgb数据。
在wfreerdp中,首先创建primary为自定义数据类型,其中包含一个dib,和这个这个dib对应的hdc,其实就是在windows开发中熟悉的自定义bitmap画布。后来调用gdi_init_ex把bitmap的rgba部分传给freerdp,让其绘制。
wf_end_paint
获取gdi->primary->hdc->hwnd->ninvalid参数,这个参数表达的意思是一帧中,有多少个变化的区域。后面会合并这几个区域,并设置windows窗口中的绘制区域为invalid,生成WM_PAINT事件,在WM_PAINT事件中,把上述primary中的bitmap画板StretchBlt到画板中。
细节
- windows的event loop中需要实现defalt和WM_DESTROY事件
- swf_update_canvas_diff主要是同步rdp内部图像和windows绘制区域的大小。
- windows event loop中获取自定义rdpContext的方法是GetWindowLongPtr函数,当然需要在wf_post_connect中先调用SetWindowLongPtr函数。
代码:
https://github.com/Niap/swfreerdp-client
仅实现rdp图像的绘制。
代码均拷贝于 wfreerdp,可能有部分残留
---------------------------------------------------KeyBoard&mouse 的支持----------------------------------------------
wfreerdp中,生成一个线程来hook住键盘事件,并且是以 SetWindowsHookEx函数方式来hook键盘事件。我们我们在测试中就没必要这样实现了。只需要添加WM_KEYUP、WM_KEYDOWN事件的处理即可。
在event_proc中添加的代码
#define X_POS(lParam) ((UINT16) (lParam & 0xFFFF))
#define Y_POS(lParam) ((UINT16) ((lParam >> 16) & 0xFFFF))
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
rdp_scancode = (lParam >> 16) & 0xff;
input = swfc->context.input;
freerdp_input_send_keyboard_event_ex(input, TRUE,rdp_scancode);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
rdp_scancode = lParam >> 16 & 0xff;
input = swfc->context.input;
freerdp_input_send_keyboard_event_ex(input, FALSE, rdp_scancode);
break;
case WM_MOUSEMOVE:
input = swfc->context.input;
freerdp_input_send_mouse_event(input, PTR_FLAGS_MOVE, X_POS(lParam), Y_POS(lParam));
break;
case WM_LBUTTONDOWN:
input = swfc->context.input;
freerdp_input_send_mouse_event(input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1,X_POS(lParam), Y_POS(lParam));
break;
case WM_LBUTTONUP:
input = swfc->context.input;
freerdp_input_send_mouse_event(input, PTR_FLAGS_BUTTON1, X_POS(lParam), Y_POS(lParam));
break;
case WM_RBUTTONDOWN:
input = swfc->context.input;
freerdp_input_send_mouse_event(input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2,X_POS(lParam), Y_POS(lParam));
break;
case WM_RBUTTONUP:
input = swfc->context.input;
freerdp_input_send_mouse_event(input, PTR_FLAGS_BUTTON2, X_POS(lParam), Y_POS(lParam));
break;
---------------------------------------------------REMOTE APP 的支持----------------------------------------------
freerdp中的channel是使用freerdp和rdp server之间通讯的桥梁,像剪切板这样的功能也是通过channel来实现的,wfreerdp再2.0.0-rc4中已经添加了对rail的支持,虽然支持的并不是很好。
wf_client中需要添加的代码
再pre_connect函数中添加,意思是加载channel,当然,如果没有
if (!freerdp_client_load_addins(context->channels, instance->settings))
return -1;
PubSub_SubscribeChannelConnected(instance->context->pubSub,swf_OnChannelConnectedEventHandler);
PubSub_SubscribeChannelDisconnected(instance->context->pubSub,swf_OnChannelDisconnectedEventHandler);
完成 swf_OnChannelConnectedEventHandler
其中,只需要相应RAIL_SVC_CHANNEL_NAME 这个channel就可以了
完成
当然,再这里需要再wfreerdp 的context中先添加结构体成员rail类型为:RailClientContext,这里的回调可以照抄wfreerdp,实际上真实有用的,也只有wf_rail_server_handshake函数。
swfc->rail = rail;
rail->custom = (void*)swfc;
rail->ServerExecuteResult = wf_rail_server_execute_result;
rail->ServerSystemParam = wf_rail_server_system_param;
rail->ServerHandshake = wf_rail_server_handshake;
rail->ServerHandshakeEx = wf_rail_server_handshake_ex;
rail->ServerLocalMoveSize = wf_rail_server_local_move_size;
rail->ServerMinMaxInfo = wf_rail_server_min_max_info;
rail->ServerLanguageBarInfo = wf_rail_server_language_bar_info;
rail->ServerGetAppIdResponse = wf_rail_server_get_appid_response;
具体代码,可以参考 supportRail分支。