-
Notifications
You must be signed in to change notification settings - Fork 790
/
win32thread.cpp
333 lines (303 loc) · 10.2 KB
/
win32thread.cpp
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*
MFC Thread data type
Created Jan 1998, Mark Hammond (MHammond@skippinet.com.au)
Note that this source file contains embedded documentation.
This documentation consists of marked up text inside the
C comments, and is prefixed with an '@' symbol. The source
files are processed by a tool called "autoduck" which
generates Windows .hlp files.
@doc
*/
#include "stdafx.h"
#include "win32ui.h"
#include "win32win.h"
extern BOOL HookWindowsMessages();
extern BOOL Win32uiPreTranslateMessage(MSG *pMsg);
class CPythonWinThread : public CWinThread {
public:
CPythonWinThread()
{
obFunc = NULL;
obArgs = NULL;
}
CPythonWinThread(AFX_THREADPROC pfnThreadProc) : CWinThread(pfnThreadProc, NULL)
{
m_pThreadParams = this;
obFunc = NULL;
obArgs = NULL;
}
~CPythonWinThread()
{
Py_XDECREF(obFunc);
Py_XDECREF(obArgs);
Python_delete_assoc(this);
}
virtual BOOL PreTranslateMessage(MSG *pMsg)
{
if (Win32uiPreTranslateMessage(pMsg))
return TRUE;
else
return CWinThread::PreTranslateMessage(pMsg);
}
virtual BOOL InitInstance()
{
HookWindowsMessages();
CVirtualHelper helper("InitInstance", this);
if (helper.HaveHandler() && helper.call()) {
BOOL ret;
helper.retval(ret);
// The main app InitInstance assumes a zero return.
return (ret == 0);
}
else {
helper.release_full();
return CWinThread::InitInstance();
}
}
virtual int ExitInstance()
{
CVirtualHelper helper("ExitInstance", this);
if (helper.HaveHandler() && helper.call()) {
int ret;
helper.retval(ret);
return ret;
}
else {
helper.release_full();
return CWinThread::ExitInstance();
}
}
virtual int Run()
{
int ret;
CVirtualHelper helper("Run", this);
if (!helper.HaveHandler()) {
helper.release_full(); // important
ret = CWinThread::Run();
}
else {
helper.call();
helper.retval(ret);
}
return ret;
}
PyObject *obFunc;
PyObject *obArgs;
};
void CProtectedWinThread::PumpIdle()
{
long lIdleCount = 0;
while (OnIdle(lIdleCount++));
return;
}
void CProtectedWinThread::PumpMessages()
{
ASSERT_VALID(this);
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
#if _MFC_VER >= 0x0710
_AFX_THREAD_STATE *pState = AfxGetThreadState();
MSG &msgCur = pState->m_msgCur;
#else
MSG &msgCur = m_msgCur;
#endif /* _MFC_VER_ */
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;) {
// phase1: check to see if we can do idle work
while (bIdle && !::PeekMessage(&msgCur, NULL, NULL, NULL, PM_NOREMOVE)) {
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
// phase2: pump messages while available
do {
// pump message, but quit on WM_QUIT
if (!PumpMessage()) {
#if defined(_DEBUG)
#if _MFC_VER < 0x0710
m_nDisablePumpCount--; // application must NOT die
#else
pState->m_nDisablePumpCount--; // application must NOT die
#endif
#endif
return;
}
// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&msgCur)) {
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
bool CProtectedWinThread::PumpWaitingMessages(UINT firstMsg, UINT lastMsg)
{
bool bHaveQuit = false;
MSG msg;
if (::PeekMessage(&msg, NULL, firstMsg, lastMsg, PM_REMOVE)) {
if (msg.message == WM_QUIT)
bHaveQuit = true;
::DispatchMessage(&msg);
}
return bHaveQuit;
}
unsigned int ThreadWorkerEntryPoint(LPVOID lpvoid)
{
CPythonWinThread *pThis = (CPythonWinThread *)lpvoid;
CEnterLeavePython _celp;
PyObject *result = PyObject_CallObject(pThis->obFunc, pThis->obArgs);
if (result == NULL) {
if (PyErr_Occurred() == PyExc_SystemExit)
PyErr_Clear();
else {
ExceptionHandler(EHA_PRINT_ERROR, _T("Unhandled exception in thread"));
}
}
else
Py_DECREF(result);
// Cleanup thread state?
return 0;
}
PyCWinThread::PyCWinThread() {}
PyCWinThread::~PyCWinThread() {}
CWinThread *GetCWinThreadPtr(PyObject *self)
{
return (CWinThread *)ui_assoc_object::GetGoodCppObject(self, &PyCWinThread::type);
}
CProtectedWinThread *GetCProtectedWinThreadPtr(PyObject *self)
{
return (CProtectedWinThread *)ui_assoc_object::GetGoodCppObject(self, &PyCWinThread::type);
}
// @pymethod <o PyCWinThread>|win32ui|CreateThread|Creates a new <o PyCWinThread> object
PyObject *PyCWinThread::create(PyObject *self, PyObject *args)
{
CPythonWinThread *pThread;
PyObject *obFunc, *obArgs = Py_None;
if (PyArg_ParseTuple(args, "|:CreateThread")) {
pThread = new CPythonWinThread();
}
else if (PyArg_ParseTuple(args, "O|O:CreateThread", &obFunc, &obArgs)) {
PyErr_Clear();
if (!PyCallable_Check(obFunc)) {
PyErr_SetString(PyExc_TypeError, "First argument must be a callable object");
return NULL;
}
pThread = new CPythonWinThread(ThreadWorkerEntryPoint);
pThread->obFunc = obFunc;
pThread->obArgs = obArgs;
Py_INCREF(obFunc);
Py_INCREF(obArgs);
}
else {
PyErr_Clear();
PyErr_SetString(PyExc_TypeError, "Must pass no arguments, or a function and optional arguments");
return NULL;
}
return ui_assoc_object::make(PyCWinThread::type, pThread, TRUE);
}
// @pymethod |PyCWinThread|PumpIdle|Pumps all idle messages.
static PyObject *ui_thread_pump_idle(PyObject *self, PyObject *args)
{
CHECK_NO_ARGS2(args, PumpIdle);
CProtectedWinThread *pThread = GetCProtectedWinThreadPtr(self);
if (!pThread)
return NULL;
GUI_BGN_SAVE;
pThread->PumpIdle();
GUI_END_SAVE;
RETURN_NONE;
}
// @pymethod |PyCWinThread|SetMainFrame|Sets the threads main frame
static PyObject *ui_thread_set_main_frame(PyObject *self, PyObject *args)
{
PyObject *wndObject;
if (!PyArg_ParseTuple(args, "O:SetMainFrame",
&wndObject)) // @pyparm <o PyCWnd>|mainFrame||The applications main frame.
return NULL;
CWinThread *pThread = GetCWinThreadPtr(self);
if (!pThread)
return NULL;
if (wndObject == Py_None) {
// @comm You can pass None to this function to reset the main frame.
pThread->m_pMainWnd = NULL; // Should I free this? I don't think so!
}
else {
CWnd *pMainWnd = GetWndPtr(wndObject);
if (!pMainWnd)
return NULL;
pThread->m_pMainWnd = pMainWnd;
}
RETURN_NONE;
}
// @pymethod |PyCWinThread|SetThreadPriority|Sets the threads priority. Returns TRUE if successful.
static PyObject *ui_thread_set_thread_priority(PyObject *self, PyObject *args)
{
int priority;
if (!PyArg_ParseTuple(args, "i:SetThreadPriority",
&priority)) // @pyparm <o PyCWnd>|priority||The threads priority.
return NULL;
CWinThread *pThread = GetCWinThreadPtr(self);
if (!pThread)
return NULL;
long rc = pThread->SetThreadPriority(priority);
return PyLong_FromLong(rc);
}
// @pymethod int|PyCWinThread|Run|Starts the message pump. Advanced users only
static PyObject *ui_thread_run(PyObject *self, PyObject *args)
{
CHECK_NO_ARGS2(args, "Run");
CWinThread *pThread = GetCWinThreadPtr(self);
if (!pThread)
return NULL;
GUI_BGN_SAVE;
long rc = pThread->CWinThread::Run();
GUI_END_SAVE;
return PyLong_FromLong(rc);
}
// @pymethod |PyCWinThread|CreateThread|Creates the actual thread behind the thread object.
static PyObject *ui_thread_create_thread(PyObject *self, PyObject *args)
{
DWORD createFlags = 0;
UINT stackSize = 0;
if (!PyArg_ParseTuple(args, "|li:CreateThread", &createFlags, &stackSize))
return NULL;
CWinThread *pThread = GetCWinThreadPtr(self);
if (!pThread)
return NULL;
GUI_BGN_SAVE;
BOOL ok = pThread->CreateThread(createFlags, stackSize);
GUI_END_SAVE;
if (!ok)
RETURN_ERR("CreateThread failed");
RETURN_NONE;
}
// @pymethod |PyCWinThread|PumpMessages|Pumps all messages to the application until a WM_QUIT message is received.
static PyObject *ui_thread_pump_messages(PyObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":PumpMessages"))
return NULL;
CProtectedWinThread *pThread = GetProtectedThread();
if (!pThread)
return NULL;
GUI_BGN_SAVE;
pThread->PumpMessages();
GUI_END_SAVE;
RETURN_NONE;
// @comm This allows an application which is performing a long operation to dispatch paint messages during the
// operation.
}
// @object PyCWinThread|An application class. Encapsulates an MFC <c CWinThread> class
static struct PyMethodDef PyCWinThread_methods[] = {
{"CreateThread", ui_thread_create_thread,
1}, // @pymeth CreateThread|Creates the actual thread behind the thread object.
{"PumpIdle", ui_thread_pump_idle, 1}, // @pymeth PumpIdle|Pumps idle messages.
{"PumpMessages", ui_thread_pump_messages,
1}, // @pymeth PumpMessages|Pumps all messages to the application until a WM_QUIT message is received.
{"Run", ui_thread_run, 1}, // @pymeth Run|Starts the main application message pump.
{"SetMainFrame", ui_thread_set_main_frame, 1}, // @pymeth SetMainFrame|Sets the C++ applications main frame
{"SetThreadPriority", ui_thread_set_thread_priority, 1}, // @pymeth SetThreadPriority|Sets the threads priority
{NULL, NULL}};
ui_type_CObject PyCWinThread::type("PyCWinThread", &PyCCmdTarget::type, RUNTIME_CLASS(CWinThread), sizeof(PyCWinThread),
PYOBJ_OFFSET(PyCWinThread), PyCWinThread_methods, GET_PY_CTOR(PyCWinThread));