0%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <D3D9.h>
#include <DirectXMath.h>

struct Point
{
Point(const int32_t x, const int32_t y)
: _x(x)
, _y(y)
{
}

int32_t _x;
int32_t _y;
};

struct PointF
{
PointF(const double x, const double y)
: _x(x)
, _y(y)
{
}

double _x;
double _y;
};

PointF screen_pos_to_d3d_world_pos(HWND hWnd, IDirect3DDevice9* device, const Point& pt)
{
using namespace DirectX;

XMVECTOR screenPos = XMVectorSet(pt.x(), pt.y(), 0, 0);
XMVECTOR worldPos;

// 获取客户区宽高
RECT clientRect;
GetClientRect(hWnd, &clientRect);

// 获取视口信息
D3DVIEWPORT9 viewport;
device->GetViewport(&viewport);

// 获取投影矩阵
D3DMATRIX projectionMatrix;
device->GetTransform(D3DTS_PROJECTION, &projectionMatrix);

// 获取世界矩阵
D3DMATRIX worldMatrix;
device->GetTransform(D3DTS_WORLD, &worldMatrix);

// 获取视图矩阵
D3DMATRIX viewMatrix;
device->GetTransform(D3DTS_VIEW, &viewMatrix);

XMMATRIX xmProjectionMatrix = XMLoadFloat4x4((XMFLOAT4X4*)&projectionMatrix);
XMMATRIX xmWorldMatrix = XMLoadFloat4x4((XMFLOAT4X4*)&worldMatrix);
XMMATRIX xmViewMatrix = XMLoadFloat4x4((XMFLOAT4X4*)&viewMatrix);

worldPos = XMVector3Unproject(
screenPos,
viewport.X,
viewport.Y,
clientRect.right - clientRect.left,
clientRect.bottom - clientRect.top,
viewport.MinZ,
viewport.MaxZ,
xmProjectionMatrix,
xmViewMatrix,
xmWorldMatrix
);

return PointF(XMVectorGetX(worldPos), XMVectorGetY(worldPos));
}

摘要

我希望在lua脚本中执行http请求,然后在注册的回调函数中异步的处理response,http请求任务以队列的形式进行排队,每次只有一个任务在执行。

lua本身是没有提供多线程支持的,虽然它支持协程,但本质是在单个线程内进行切换,本质不是并行。

lua可在不同线程中创建独立的lua状态机,而lua_newthread可根据已有lua状态机创建一个有独立调用栈的独立状态机(它们共享全局环境、注册表和栈空间),可以利用这两个特性来实现异步回调。

实现方法

二进制模块中使用libcurl库来执行http请求

初始化

模块命名为async_request,在luaopen_async_request中完成创建模块、初始化libcurl、创建任务线程以及等待创建新线程使用的lua状态机。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
static void routine(std::promise<void*>* p)
{
RequestContext next_context;

// g_main_lua_state是主线程的lua状态机
// 根据它来创建子线程的lua状态机,它们共享全局环境、注册表和栈空间
// 但新的lua状态机有独立的调用栈,可独立运行
g_routine_lua_state = lua_newthread(g_main_lua_state);

// 把新状态机记录到全局变量中防止被回收
// 同时也是平衡主线程lua状态机的栈
lua_setglobal(g_main_lua_state, "async_request_thread");

// 初始化完毕,唤醒主线程
p->set_value(g_routine_lua_state);

for (;;)
{
{
std::unique_lock<std::mutex> lock(g_mutex);
g_cv.wait_for(lock, std::chrono::milliseconds(100), [] {
return !g_queue.empty() || g_quit_flag;
});

// 退出指令
if (g_quit_flag) {
break;
}

if (g_queue.empty()) {
continue;
}

next_context = g_queue.front();
g_queue.pop();
}

func_request(next_context);
}

// 等待子线程lua状态机运行完毕
while (lua_status(g_routine_lua_state) != LUA_OK && lua_status(g_routine_lua_state) != LUA_YIELD)
{

}

// 关闭子线程lua状态机
lua_close(g_routine_lua_state);
}

int LUA_API luaopen_async_request(lua_State* L)
{
luaL_newlib(L, AsyncRequestModuleMethods);

char full_path[MAX_PATH] = { 0 };
char abs_full_path[MAX_PATH] = { 0 };

// 这里是为了从async_request.dll模块的所在目录去加载libcurl.dll模块,libcurl.dll以延迟加载的方式导入
if (GetModuleFileNameA(GetModuleHandleA("async_request.dll"), full_path, MAX_PATH)) {
if (GetFullPathNameA(full_path, MAX_PATH, abs_full_path, nullptr)) {
std::string dll_search_path = std::filesystem::path::path(abs_full_path).parent_path().string();
SetDllDirectoryA(dll_search_path.c_str());
}
}

// 以g_routine_lua_state是否为空来判断是否已经创建了子线程的lua状态机
if (g_routine_lua_state) {
return 1;
}

// 初始化libcurl
curl_global_init(CURL_GLOBAL_ALL);

std::promise<void*> p;
std::future<void*> f = p.get_future();

// 创建子线程,并等待子线程初始化完毕
g_main_lua_state = L;
g_routine_thread = std::thread(routine, &p);

f.wait();

//luaL_newmetatable(L, "async_request");
//lua_pushcfunction(L, luaL_async_request_destroy);
//lua_setfield(L, -2, "__gc");
//lua_setmetatable(L, -2);

return 1;
}

注册回调函数

阅读全文 »

重新编译QWebEngine

因为腾讯的WebRTC需要h264解码,官方的QWebEngine默认是没有启用的,所以需要用-webengine-proprietary-codecs开关重新编译QWebEngine

https://doc.qt.io/qt-5/qtwebengine-features.html#audio-and-video-codecs

确定PyQt版本

比如目前用的是5.15.2,需要更新打包环境的相关包:

1
2
3
4
5
6
pip install PyQt5==5.15.2
pip install PyQt5-Qt5==5.15.2
pip install PyQt5-sip==12.12.0
pip install PyQt5-stubs==5.15.2.0
pip install PyQtWebEngine==5.15.2
pip install PyQtWebEngine-Qt5==5.15.2

安装编译环境依赖

1. 安装qt framework and tool

5.15.2是没有官方离线包的,所以只能通过官方的安装工具来下载,选择以下组件(Sources必须选择,因为编译需要源码)

 Qt 5.15.2

阅读全文 »

F12调出DevTools之后,按快捷键Ctrl + Shift + P输入命令Capture full size screenshot