You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lmessages.pas 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. { Copyright (C) 2005 Bas Steendijk and Peter Green
  2. For conditions of distribution and use, see copyright notice in zlib_license.txt
  3. which is included in the package
  4. ----------------------------------------------------------------------------- }
  5. //this unit provides a rough approximation of windows messages on linux
  6. //it is usefull for multithreaded applications on linux to communicate back to
  7. //the main lcore thread
  8. //This unit is *nix only, on windows you should use the real thing
  9. unit lmessages;
  10. //windows messages like system based on lcore tasks
  11. interface
  12. uses pgtypes,sysutils,bsearchtree,strings,syncobjs;
  13. {$if (fpc_version < 2) or ((fpc_version=2) and ((fpc_release < 2) or ((fpc_release = 2) and (fpc_patch < 2)) ))}
  14. {$error this code is only supported under fpc 2.2.2 and above due to bugs in the eventobject code in older versions}
  15. {$endif}
  16. type
  17. lparam=taddrint;
  18. wparam=taddrint;
  19. thinstance=pointer;
  20. hicon=pointer;
  21. hcursor=pointer;
  22. hbrush=pointer;
  23. hwnd=qword; //window handles are monotonically increasing 64 bit integers,
  24. //this should allow for a million windows per second for over half
  25. //a million years!
  26. twndproc=function(ahWnd:HWND; auMsg:Integer; awParam:WPARAM; alParam:LPARAM):Integer; stdcall;
  27. twndclass=record
  28. style : dword;
  29. lpfnwndproc : twndproc;
  30. cbclsextra : integer;
  31. cbwndextra : integer;
  32. hinstance : thinstance;
  33. hicon : hicon;
  34. hcursor : hcursor;
  35. hbrbackground : hbrush;
  36. lpszmenuname : pchar;
  37. lpszclassname : pchar;
  38. end;
  39. PWNDCLASS=^twndclass;
  40. UINT=dword;
  41. WINBOOL = longbool;
  42. tTIMERPROC = procedure (ahwnd:HWND; umsg:integer; idevent:taddrint;dwtime:taddrint);stdcall;
  43. ATOM = pointer;
  44. LPCSTR = pchar;
  45. LPVOID = pointer;
  46. HMENU = pointer;
  47. HINST = pointer;
  48. TPOINT = record
  49. x : LONGint;
  50. y : LONGint;
  51. end;
  52. TMSG = record
  53. hwnd : HWND;
  54. message : UINT;
  55. wParam : WPARAM;
  56. lParam : LPARAM;
  57. time : DWORD;
  58. pt : TPOINT;
  59. end;
  60. THevent=TEventObject;
  61. const
  62. WS_EX_TOOLWINDOW = $80;
  63. WS_POPUP = longint($80000000);
  64. hinstance=nil;
  65. PM_REMOVE = 1;
  66. WM_USER = 1024;
  67. WM_TIMER = 275;
  68. INFINITE = syncobjs.infinite;
  69. function getwindowlongptr(ahwnd:hwnd;nindex:integer) : taddrint;
  70. function setwindowlongptr(ahwnd:hwnd;nindex:integer;dwNewLong : taddrint) : taddrint;
  71. function DefWindowProc(ahWnd:HWND; auMsg:Integer; awParam:WPARAM; alParam:LPARAM):Integer; stdcall;
  72. function RegisterClass(const lpWndClass:TWNDCLASS):ATOM;
  73. function CreateWindowEx(dwExStyle:DWORD; lpClassName:LPCSTR; lpWindowName:LPCSTR; dwStyle:DWORD; X:longint;Y:longint; nWidth:longint; nHeight:longint; hWndParent:HWND; hMenu:HMENU;hInstance:HINST; lpParam:LPVOID):HWND;
  74. function DestroyWindow(ahWnd:HWND):WINBOOL;
  75. function PostMessage(hWnd:HWND; Msg:UINT; wParam:WPARAM; lParam:LPARAM):WINBOOL;
  76. function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): WINBOOL;
  77. function DispatchMessage(const lpMsg: TMsg): Longint;
  78. function GetMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT): WINBOOL;
  79. function SetEvent(hEvent:THevent):WINBOOL;
  80. function CreateEvent(lpEventAttributes:PSECURITYATTRIBUTES; bManualReset:WINBOOL; bInitialState:WINBOOL; lpName:pchar):tHevent;
  81. function terminatethread(threadhandle : tthreadid;dummy:integer) : boolean;
  82. function waitforsingleevent(event:thevent;timeout:cardinal) : twaitresult;
  83. function SetTimer(ahWnd:HWND; nIDEvent:taddrint; uElapse:UINT; lpTimerFunc:tTIMERPROC):UINT;
  84. function KillTimer(ahWnd:HWND; uIDEvent:taddrint):WINBOOL;
  85. procedure init;
  86. implementation
  87. uses
  88. baseunix,unix,lcore,unixutil;//,safewriteln;
  89. {$i unixstuff.inc}
  90. type
  91. tmessageintransit = class
  92. msg : tmsg;
  93. next : tmessageintransit;
  94. end;
  95. tthreaddata = class
  96. messagequeue : tmessageintransit;
  97. messageevent : teventobject;
  98. waiting : boolean;
  99. lcorethread : boolean;
  100. nexttimer : ttimeval;
  101. threadid : integer;
  102. end;
  103. twindow=class
  104. hwnd : hwnd;
  105. extrawindowmemory : pointer;
  106. threadid : tthreadid;
  107. windowproc : twndproc;
  108. end;
  109. var
  110. structurelock : tcriticalsection;
  111. threaddata : thashtable;
  112. windowclasses : thashtable;
  113. lcorelinkpipesend : integer;
  114. lcorelinkpiperecv : tlasio;
  115. windows : thashtable;
  116. //I would rather things crash immediately
  117. //if they use an insufficiant size type
  118. //than crash after over four billion
  119. //windows have been made ;)
  120. nextwindowhandle : qword = $100000000;
  121. {$i ltimevalstuff.inc}
  122. //findthreaddata should only be called while holding the structurelock
  123. function findthreaddata(threadid : integer) : tthreaddata;
  124. begin
  125. result := tthreaddata(findtree(@threaddata,inttostr(threadid)));
  126. if result = nil then begin
  127. result := tthreaddata.create;
  128. result.messageevent := teventobject.create(nil,false,false,inttostr(taddrint(result)));
  129. result.nexttimer := tv_invalidtimebig;
  130. result.threadid := threadid;
  131. addtree(@threaddata,inttostr(threadid),result);
  132. end;
  133. end;
  134. //deletethreaddataifunused should only be called while holding the structurelock
  135. procedure deletethreaddataifunused(athreaddata : tthreaddata);
  136. begin
  137. //writeln('in deletethreaddataifunused');
  138. if (athreaddata <> nil) then if (athreaddata.waiting=false) and (athreaddata.messagequeue=nil) and (athreaddata.lcorethread=false) and (athreaddata.nexttimer.tv_sec=tv_invalidtimebig.tv_sec) and (athreaddata.nexttimer.tv_usec=tv_invalidtimebig.tv_usec) then begin
  139. //writeln('threaddata is unused, freeing messageevent');
  140. athreaddata.messageevent.free;
  141. //writeln('freeing thread data object');
  142. athreaddata.free;
  143. //writeln('deleting thread data object from hashtable');
  144. deltree(@threaddata,inttostr(athreaddata.threadid));
  145. //writeln('finished deleting thread data');
  146. end else begin
  147. //writeln('thread data is not unused');
  148. end;
  149. end;
  150. function getwindowlongptr(ahwnd:hwnd;nindex:integer) : taddrint;
  151. var
  152. window : twindow;
  153. begin
  154. structurelock.acquire;
  155. try
  156. window := findtree(@windows,inttostr(ahwnd));
  157. if window <> nil then begin
  158. result := paddrint(taddrint(window.extrawindowmemory)+nindex)^;
  159. end else begin
  160. result := 0;
  161. end;
  162. finally
  163. structurelock.release;
  164. end;
  165. end;
  166. function setwindowlongptr(ahwnd:hwnd;nindex:integer;dwNewLong : taddrint) : taddrint;
  167. var
  168. window : twindow;
  169. begin
  170. structurelock.acquire;
  171. try
  172. window := findtree(@windows,inttostr(ahwnd));
  173. if window <> nil then begin
  174. result := paddrint(taddrint(window.extrawindowmemory)+nindex)^;
  175. paddrint(taddrint(window.extrawindowmemory)+nindex)^ := dwnewlong;
  176. end else begin
  177. result := 0;
  178. end;
  179. finally
  180. structurelock.release;
  181. end;
  182. end;
  183. function DefWindowProc(ahWnd:HWND; auMsg:Integer; awParam:WPARAM; alParam:LPARAM):Integer; stdcall;
  184. begin
  185. result := 0;
  186. end;
  187. function strdup(s:pchar) : pchar;
  188. begin
  189. //swriteln('in strdup, about to allocate memory');
  190. result := getmem(strlen(s)+1);
  191. //swriteln('about to copy string');
  192. strcopy(s,result);
  193. //swriteln('leaving strdup');
  194. end;
  195. function RegisterClass(const lpWndClass:TWNDCLASS):ATOM;
  196. var
  197. storedwindowclass:pwndclass;
  198. begin
  199. structurelock.acquire;
  200. try
  201. //swriteln('in registerclass, about to check for duplicate window class');
  202. storedwindowclass := findtree(@windowclasses, lpwndclass.lpszclassname);
  203. if storedwindowclass <> nil then begin
  204. if comparebyte(storedwindowclass^,lpwndclass,sizeof(twndclass)-sizeof(pchar)-sizeof(pchar)) <> 0 then begin
  205. //swriteln('duplicate window class registered with different settings');
  206. raise exception.create('duplicate window class registered with different settings');
  207. end else begin
  208. //swriteln('duplicate window class registered with same settings, tollerated');
  209. end;
  210. end else begin
  211. //swriteln('about to allocate memory for new windowclass');
  212. storedwindowclass := getmem(sizeof(twndclass));
  213. //swriteln('about to copy windowclass from parameter');
  214. move(lpwndclass,storedwindowclass^,sizeof(twndclass));
  215. //swriteln('about to copy strings');
  216. if lpwndclass.lpszmenuname <> nil then storedwindowclass.lpszmenuname := strdup(lpwndclass.lpszmenuname);
  217. if lpwndclass.lpszclassname <> nil then storedwindowclass.lpszclassname := strdup(lpwndclass.lpszclassname);
  218. //swriteln('about to add result to list of windowclasses');
  219. addtree(@windowclasses,lpwndclass.lpszclassname,storedwindowclass);
  220. end;
  221. //swriteln('about to return result');
  222. result := storedwindowclass;
  223. //swriteln('leaving registerclass');
  224. finally
  225. structurelock.release;
  226. end;
  227. end;
  228. function CreateWindowEx(dwExStyle:DWORD; lpClassName:LPCSTR; lpWindowName:LPCSTR; dwStyle:DWORD; X:longint;Y:longint; nWidth:longint; nHeight:longint; hWndParent:HWND; hMenu:HMENU;hInstance:HINST; lpParam:LPVOID):HWND;
  229. var
  230. wndclass : pwndclass;
  231. tm : tthreadmanager;
  232. window : twindow;
  233. begin
  234. structurelock.acquire;
  235. try
  236. window := twindow.create;
  237. window.hwnd := nextwindowhandle;
  238. result := window.hwnd;
  239. nextwindowhandle := nextwindowhandle + 1;
  240. addtree(@windows,inttostr(window.hwnd),window);
  241. wndclass := findtree(@windowclasses,lpclassname);
  242. window.extrawindowmemory := getmem(wndclass.cbwndextra);
  243. getthreadmanager(tm);
  244. window.threadid := tm.GetCurrentThreadId;
  245. window.windowproc := wndclass.lpfnwndproc;
  246. finally
  247. structurelock.release;
  248. end;
  249. end;
  250. function DestroyWindow(ahWnd:HWND):WINBOOL;
  251. var
  252. window : twindow;
  253. windowthreaddata : tthreaddata;
  254. currentmessage : tmessageintransit;
  255. prevmessage : tmessageintransit;
  256. begin
  257. //writeln('started to destroy window');
  258. structurelock.acquire;
  259. try
  260. window := twindow(findtree(@windows,inttostr(ahwnd)));
  261. if window <> nil then begin
  262. freemem(window.extrawindowmemory);
  263. //writeln('aboute to delete window from windows structure');
  264. deltree(@windows,inttostr(ahwnd));
  265. //writeln('deleted window from windows structure');
  266. windowthreaddata := tthreaddata(findtree(@threaddata,inttostr(window.threadid)));
  267. if windowthreaddata <> nil then begin
  268. //writeln('found thread data scanning for messages to clean up');
  269. currentmessage := windowthreaddata.messagequeue;
  270. prevmessage := nil;
  271. while currentmessage <> nil do begin
  272. while (currentmessage <> nil) and (currentmessage.msg.hwnd = ahwnd) do begin
  273. if prevmessage = nil then begin
  274. windowthreaddata.messagequeue := currentmessage.next;
  275. end else begin
  276. prevmessage.next := currentmessage.next;
  277. end;
  278. currentmessage.free;
  279. if prevmessage = nil then begin
  280. currentmessage := windowthreaddata.messagequeue;
  281. end else begin
  282. currentmessage := prevmessage.next;
  283. end;
  284. end;
  285. if currentmessage <> nil then begin
  286. prevmessage := currentmessage;
  287. currentmessage := currentmessage.next;
  288. end;
  289. end;
  290. //writeln('deleting thread data structure if it is unused');
  291. deletethreaddataifunused(windowthreaddata);
  292. end else begin
  293. //writeln('there is no thread data to search for messages to cleanup');
  294. end;
  295. //writeln('freeing window');
  296. window.free;
  297. result := true;
  298. end else begin
  299. result := false;
  300. end;
  301. finally
  302. structurelock.release;
  303. end;
  304. //writeln('window destroyed');
  305. end;
  306. function PostMessage(hWnd:HWND; Msg:UINT; wParam:WPARAM; lParam:LPARAM):WINBOOL;
  307. var
  308. threaddata : tthreaddata;
  309. message : tmessageintransit;
  310. messagequeueend : tmessageintransit;
  311. window : twindow;
  312. begin
  313. structurelock.acquire;
  314. try
  315. window := findtree(@windows,inttostr(hwnd));
  316. if window <> nil then begin
  317. threaddata := findthreaddata(window.threadid);
  318. message := tmessageintransit.create;
  319. message.msg.hwnd := hwnd;
  320. message.msg.message := msg;
  321. message.msg.wparam := wparam;
  322. message.msg.lparam := lparam;
  323. if threaddata.lcorethread then begin
  324. //swriteln('posting message to lcore thread');
  325. fdwrite(lcorelinkpipesend,message,sizeof(message));
  326. end else begin
  327. //writeln('posting message to non lcore thread');
  328. if threaddata.messagequeue = nil then begin
  329. threaddata.messagequeue := message;
  330. end else begin
  331. messagequeueend := threaddata.messagequeue;
  332. while messagequeueend.next <> nil do begin
  333. messagequeueend := messagequeueend.next;
  334. end;
  335. messagequeueend.next := message;
  336. end;
  337. //writeln('message added to queue');
  338. if threaddata.waiting then threaddata.messageevent.setevent;
  339. end;
  340. result := true;
  341. end else begin
  342. result := false;
  343. end;
  344. finally
  345. structurelock.release;
  346. end;
  347. end;
  348. function gettickcount : dword;
  349. var
  350. result64: integer;
  351. tv : ttimeval;
  352. begin
  353. gettimeofday(tv);
  354. result64 := (tv.tv_sec*1000)+(tv.tv_usec div 1000);
  355. result := result64;
  356. end;
  357. function DispatchMessage(const lpMsg: TMsg): Longint;
  358. var
  359. timerproc : ttimerproc;
  360. window : twindow;
  361. windowproc : twndproc;
  362. begin
  363. ////swriteln('in dispatchmessage, msg.hwnd='+inttohex(taddrint(lpmsg.hwnd),16));
  364. if (lpmsg.lparam <> 0) and (lpmsg.message = WM_TIMER) then begin
  365. timerproc := ttimerproc(lpmsg.lparam);
  366. timerproc(lpmsg.hwnd,lpmsg.message,lpmsg.wparam,gettickcount);
  367. result := 0;
  368. end else begin
  369. structurelock.acquire;
  370. try
  371. window := findtree(@windows,inttostr(lpmsg.hwnd));
  372. //we have to get the window procedure while the structurelock
  373. //is still held as the window could be destroyed from another thread
  374. //otherwise.
  375. if window <> nil then begin
  376. windowproc := window.windowproc;
  377. end else begin
  378. windowproc := nil;
  379. end;
  380. finally
  381. structurelock.release;
  382. end;
  383. if assigned(windowproc) then begin
  384. result := windowproc(lpmsg.hwnd,lpmsg.message,lpmsg.wparam,lpmsg.lparam);
  385. end else begin
  386. result := -1;
  387. end;
  388. end;
  389. end;
  390. procedure processtimers;
  391. begin
  392. end;
  393. function GetMessageinternal(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT; wremovemsg : UINT;peek:boolean): WINBOOL;
  394. var
  395. tm : tthreadmanager;
  396. threaddata : tthreaddata;
  397. message : tmessageintransit;
  398. nowtv : ttimeval;
  399. timeouttv : ttimeval;
  400. timeoutms : int64;
  401. begin
  402. if hwnd <> 0 then raise exception.create('getting messages for an individual window is not supported');
  403. if (wmsgfiltermin <> 0) or (wmsgfiltermax <> 0) then raise exception.create('message filtering is not supported');
  404. structurelock.acquire;
  405. result := true;
  406. try
  407. getthreadmanager(tm);
  408. threaddata := findthreaddata(tm.GetCurrentThreadId);
  409. if threaddata.lcorethread then raise exception.create('get/peek message cannot be used in the lcore thread');
  410. message := threaddata.messagequeue;
  411. gettimeofday(nowtv);
  412. while (not peek) and (message=nil) and (not tv_compare(nowtv,threaddata.nexttimer)) do begin
  413. threaddata.waiting := true;
  414. structurelock.release;
  415. if (threaddata.nexttimer.tv_sec = TV_invalidtimebig.tv_sec) and (threaddata.nexttimer.tv_usec = TV_invalidtimebig.tv_usec) then begin
  416. threaddata.messageevent.waitfor(INFINITE);
  417. end else begin
  418. timeouttv := threaddata.nexttimer;
  419. timeoutms := (timeouttv.tv_sec * 1000)+(timeouttv.tv_usec div 1000);
  420. //i'm assuming the timeout is in milliseconds
  421. if (timeoutms > maxlongint) then timeoutms := maxlongint;
  422. threaddata.messageevent.waitfor(timeoutms);
  423. end;
  424. structurelock.acquire;
  425. threaddata.waiting := false;
  426. message := threaddata.messagequeue;
  427. gettimeofday(nowtv);
  428. end;
  429. if (message=nil) and tv_compare(nowtv,threaddata.nexttimer) then begin
  430. processtimers;
  431. end;
  432. message := threaddata.messagequeue;
  433. if message <> nil then begin
  434. lpmsg := message.msg;
  435. if wremovemsg=PM_REMOVE then begin
  436. threaddata.messagequeue := message.next;
  437. message.free;
  438. end;
  439. end else begin
  440. result :=false;
  441. end;
  442. deletethreaddataifunused(threaddata);
  443. finally
  444. structurelock.release;
  445. end;
  446. end;
  447. function GetMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT): WINBOOL;
  448. begin
  449. result := getmessageinternal(lpmsg,hwnd,wmsgfiltermin,wmsgfiltermax,PM_REMOVE,false);
  450. end;
  451. function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): WINBOOL;
  452. begin
  453. result := getmessageinternal(lpmsg,hwnd,wmsgfiltermin,wmsgfiltermax,wRemoveMsg,true);
  454. end;
  455. function SetEvent(hEvent:THevent):WINBOOL;
  456. begin
  457. hevent.setevent;
  458. result := true;
  459. end;
  460. function CreateEvent(lpEventAttributes:PSECURITYATTRIBUTES; bManualReset:WINBOOL; bInitialState:WINBOOL; lpName:pchar):tHevent;
  461. begin
  462. result := teventobject.create(lpeventattributes,bmanualreset,binitialstate,lpname);
  463. end;
  464. function terminatethread(threadhandle:tthreadid;dummy : integer) : boolean;
  465. var
  466. tm : tthreadmanager;
  467. begin
  468. getthreadmanager(tm);
  469. tm.killthread(threadhandle);
  470. result := true;
  471. end;
  472. function waitforsingleevent(event:thevent;timeout:cardinal) : twaitresult;
  473. begin
  474. result := event.waitfor(timeout);
  475. end;
  476. procedure removefrombuffer(n : integer; var buffer:string);
  477. begin
  478. if n=length(buffer) then begin
  479. buffer := '';
  480. end else begin
  481. uniquestring(buffer);
  482. move(buffer[n+1],buffer[1],length(buffer)-n);
  483. setlength(buffer,length(buffer)-n);
  484. end;
  485. end;
  486. type
  487. tsc=class
  488. procedure available(sender:tobject;error:word);
  489. end;
  490. var
  491. recvbuf : string;
  492. procedure tsc.available(sender:tobject;error:word);
  493. var
  494. message : tmessageintransit;
  495. messagebytes : array[1..sizeof(tmessageintransit)] of char absolute message;
  496. i : integer;
  497. begin
  498. //swriteln('received data on lcorelinkpipe');
  499. recvbuf := recvbuf + lcorelinkpiperecv.receivestr;
  500. while length(recvbuf) >= sizeof(tmessageintransit) do begin
  501. for i := 1 to sizeof(tmessageintransit) do begin
  502. messagebytes[i] := recvbuf[i];
  503. end;
  504. dispatchmessage(message.msg);
  505. message.free;
  506. removefrombuffer(sizeof(tmessageintransit),recvbuf);
  507. end;
  508. end;
  509. procedure init;
  510. var
  511. tm : tthreadmanager;
  512. threaddata : tthreaddata;
  513. pipeends : tfildes;
  514. sc : tsc;
  515. begin
  516. structurelock := tcriticalsection.create;
  517. getthreadmanager(tm);
  518. threaddata := findthreaddata(tm.GetCurrentThreadId);
  519. threaddata.lcorethread := true;
  520. fppipe(pipeends);
  521. lcorelinkpipesend := pipeends[1];
  522. lcorelinkpiperecv := tlasio.create(nil);
  523. lcorelinkpiperecv.dup(pipeends[0]);
  524. lcorelinkpiperecv.ondataavailable := sc.available;
  525. recvbuf := '';
  526. end;
  527. var
  528. lcorethreadtimers : thashtable;
  529. type
  530. tltimerformsg = class(tltimer)
  531. public
  532. hwnd : hwnd;
  533. id : taddrint;
  534. procedure timer(sender : tobject);
  535. end;
  536. procedure tltimerformsg.timer(sender : tobject);
  537. var
  538. msg : tmsg;
  539. begin
  540. ////swriteln('in tltimerformsg.timer');
  541. fillchar(msg,sizeof(msg),0);
  542. msg.message := WM_TIMER;
  543. msg.hwnd := hwnd;
  544. msg.wparam := ID;
  545. msg.lparam := 0;
  546. dispatchmessage(msg);
  547. end;
  548. function SetTimer(ahWnd:HWND; nIDEvent:taddrint; uElapse:UINT; lpTimerFunc:tTIMERPROC):UINT;
  549. var
  550. threaddata : tthreaddata;
  551. ltimer : tltimerformsg;
  552. tm : tthreadmanager;
  553. window : twindow;
  554. begin
  555. structurelock.acquire;
  556. try
  557. window := findtree(@windows,inttostr(ahwnd));
  558. if window= nil then raise exception.create('invalid window');
  559. threaddata := findthreaddata(window.threadid);
  560. finally
  561. structurelock.release;
  562. end;
  563. if threaddata.lcorethread then begin
  564. getthreadmanager(tm);
  565. if tm.GetCurrentThreadId <> window.threadid then raise exception.create('timers on the lcore thread may only be added and removed from the lcore thread');
  566. if ahwnd = 0 then raise exception.create('timers on the lcore thread must be associated with a window handle');
  567. if @lptimerfunc <> nil then raise exception.create('seperate timer functions are not supported');
  568. //remove preexisting timer with same ID
  569. killtimer(ahwnd,nIDEvent);
  570. ltimer := tltimerformsg.create(nil);
  571. ltimer.interval := uelapse;
  572. ltimer.id := nidevent;
  573. ltimer.hwnd := ahwnd;
  574. ltimer.enabled := true;
  575. ltimer.ontimer := ltimer.timer;
  576. addtree(@lcorethreadtimers,inttostr(taddrint(ahwnd))+' '+inttostr(nIDEvent),ltimer);
  577. result := nidevent;
  578. end else begin
  579. raise exception.create('settimer not implemented for threads other than the lcore thread');
  580. end;
  581. end;
  582. function KillTimer(ahWnd:HWND; uIDEvent:taddrint):WINBOOL;
  583. var
  584. threaddata : tthreaddata;
  585. ltimer : tltimerformsg;
  586. tm : tthreadmanager;
  587. window : twindow;
  588. begin
  589. structurelock.acquire;
  590. try
  591. window := findtree(@windows,inttostr(ahwnd));
  592. if window= nil then raise exception.create('invalid window');
  593. threaddata := findthreaddata(window.threadid);
  594. finally
  595. structurelock.release;
  596. end;
  597. if threaddata.lcorethread then begin
  598. getthreadmanager(tm);
  599. if tm.GetCurrentThreadId <> window.threadid then raise exception.create('timers on the lcore thread may only be added and remove from the lcore thread');
  600. if ahwnd = 0 then raise exception.create('timers on the lcore thread must be associated with a window handle');
  601. ltimer := tltimerformsg(findtree(@lcorethreadtimers,inttostr(taddrint(ahwnd))+' '+inttostr(uIDEvent)));
  602. if ltimer <> nil then begin
  603. deltree(@lcorethreadtimers,inttostr(taddrint(ahwnd))+' '+inttostr(uIDEvent));
  604. ltimer.free;
  605. result := true;
  606. end else begin
  607. result := false;
  608. end;
  609. end else begin
  610. raise exception.create('settimer not implemented for threads other than the lcore thread');
  611. end;
  612. end;
  613. end.