Replace obsolete/broken lcoregtklaz with new lcorelazarus
[lcore.git] / lcoremessages.pas
diff --git a/lcoremessages.pas b/lcoremessages.pas
new file mode 100644 (file)
index 0000000..8a2bd54
--- /dev/null
@@ -0,0 +1,678 @@
+{ Copyright (C) 2005 Bas Steendijk and Peter Green\r
+  For conditions of distribution and use, see copyright notice in zlib_license.txt\r
+  which is included in the package\r
+  ----------------------------------------------------------------------------- }\r
+\r
+//this unit provides a rough approximation of windows messages on linux\r
+//it is useful for multithreaded applications on linux to communicate back to\r
+//the main lcore thread\r
+//This unit is *nix only, on windows you should use the real thing\r
+\r
+unit lcoremessages;\r
+//windows messages like system based on lcore tasks\r
+interface\r
+\r
+uses pgtypes,sysutils,bsearchtree,strings,syncobjs;\r
+\r
+\r
+{$if (fpc_version < 2) or ((fpc_version=2) and ((fpc_release < 2) or ((fpc_release = 2) and (fpc_patch < 2)) ))}\r
+  {$error this code is only supported under fpc 2.2.2 and above due to bugs in the eventobject code in older versions}\r
+{$endif}\r
+\r
+type\r
+  lparam=taddrint;\r
+  wparam=taddrint;\r
+  thinstance=pointer;\r
+  hicon=pointer;\r
+  hcursor=pointer;\r
+  hbrush=pointer;\r
+  hwnd=qword; //window handles are monotonically increasing 64 bit integers,\r
+              //this should allow for a million windows per second for over half\r
+              //a million years!\r
+\r
+  twndproc=function(ahWnd:HWND; auMsg:Integer; awParam:WPARAM; alParam:LPARAM):Integer; stdcall;\r
+\r
+\r
+  twndclass=record\r
+    style : dword;\r
+    lpfnwndproc : twndproc;\r
+    cbclsextra : integer;\r
+    cbwndextra : integer;\r
+    hinstance : thinstance;\r
+    hicon : hicon;\r
+    hcursor : hcursor;\r
+    hbrbackground : hbrush;\r
+    lpszmenuname : pchar;\r
+    lpszclassname : pchar;\r
+  end;\r
+  PWNDCLASS=^twndclass;\r
+  \r
+  UINT=dword;\r
+  WINBOOL = longbool;\r
+  tTIMERPROC = procedure (ahwnd:HWND; umsg:integer; idevent:taddrint;dwtime:taddrint);stdcall;\r
+  ATOM = pointer;\r
+  LPCSTR = pchar;\r
+  LPVOID = pointer;\r
+  HMENU = pointer;\r
+  HINST = pointer;\r
+\r
+  TPOINT = record \r
+    x : LONGint; \r
+    y : LONGint; \r
+  end; \r
+  \r
+  TMSG = record \r
+    hwnd : HWND; \r
+    message : UINT; \r
+    wParam : WPARAM; \r
+    lParam : LPARAM; \r
+    time : DWORD; \r
+    pt : TPOINT;\r
+  end; \r
+  THevent=TEventObject;\r
+const\r
+  WS_EX_TOOLWINDOW = $80;\r
+  WS_POPUP = longint($80000000);\r
+  CW_USEDEFAULT=$80000000;\r
+  hinstance=nil;\r
+  PM_REMOVE = 1;\r
+  WM_USER = 1024;\r
+  WM_TIMER = 275;\r
+  INFINITE = syncobjs.infinite;\r
+\r
+function getwindowlongptr(ahwnd:hwnd;nindex:integer) : taddrint;\r
+function setwindowlongptr(ahwnd:hwnd;nindex:integer;dwNewLong : taddrint) : taddrint;\r
+function DefWindowProc(ahWnd:HWND; auMsg:Integer; awParam:WPARAM; alParam:LPARAM):Integer; stdcall;\r
+function RegisterClass(const lpWndClass:TWNDCLASS):ATOM;\r
+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;\r
+function DestroyWindow(ahWnd:HWND):WINBOOL;\r
+function PostMessage(hWnd:HWND; Msg:UINT; wParam:WPARAM; lParam:LPARAM):WINBOOL;\r
+function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): WINBOOL;\r
+function DispatchMessage(const lpMsg: TMsg): Longint;\r
+function GetMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT): WINBOOL;\r
+function SetEvent(hEvent:THevent):WINBOOL;\r
+function CreateEvent(lpEventAttributes:PSECURITYATTRIBUTES; bManualReset:WINBOOL; bInitialState:WINBOOL; lpName:pchar):tHevent;\r
+function terminatethread(threadhandle : tthreadid;dummy:integer) : boolean;\r
+function waitforsingleevent(event:thevent;timeout:cardinal) : twaitresult;\r
+function SetTimer(ahWnd:HWND; nIDEvent:taddrint; uElapse:UINT; lpTimerFunc:tTIMERPROC):UINT;\r
+function KillTimer(ahWnd:HWND; uIDEvent:taddrint):WINBOOL;\r
+\r
+procedure init;\r
+\r
+implementation\r
+uses\r
+  baseunix,unix,lcore,unixutil,ltimevalstuff,sockets;//,safewriteln;\r
+{$i unixstuff.inc}\r
+\r
+type\r
+  \r
+  tmessageintransit = class\r
+    msg : tmsg;\r
+    next : tmessageintransit;\r
+  end;\r
+\r
+  tthreaddata = class\r
+    messagequeue : tmessageintransit;\r
+    messageevent : teventobject;\r
+    waiting : boolean;\r
+    lcorethread : boolean;\r
+    nexttimer : ttimeval;\r
+    threadid : tthreadid;\r
+  end;\r
+  twindow=class\r
+    hwnd : hwnd;\r
+    extrawindowmemory : pointer;\r
+    threadid : tthreadid;\r
+    windowproc : twndproc;\r
+  end;\r
+\r
+var\r
+  structurelock : tcriticalsection;\r
+  threaddata : thashtable;\r
+  windowclasses : thashtable;\r
+  lcorelinkpipesend : integer;\r
+  lcorelinkpiperecv : tlasio;\r
+  windows : thashtable;\r
+  //I would rather things crash immediately\r
+  //if they use an insufficient size type\r
+  //than crash after over four billion\r
+  //windows have been made ;)\r
+  nextwindowhandle : qword = $100000000;\r
+\r
+\r
+//findthreaddata should only be called while holding the structurelock\r
+function findthreaddata(threadid : tthreadid) : tthreaddata;\r
+begin\r
+  result := tthreaddata(findtree(@threaddata,inttostr(taddrint(threadid))));\r
+  if result = nil then begin\r
+    result := tthreaddata.create;\r
+    result.messageevent := teventobject.create(nil,false,false,inttostr(taddrint(result)));\r
+    result.nexttimer := tv_invalidtimebig;\r
+    result.threadid := threadid;\r
+    addtree(@threaddata,inttostr(taddrint(threadid)),result);\r
+  end;\r
+end;\r
+\r
+//deletethreaddataifunused should only be called while holding the structurelock\r
+procedure deletethreaddataifunused(athreaddata : tthreaddata);\r
+begin\r
+  //writeln('in deletethreaddataifunused');\r
+  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\r
+    //writeln('threaddata is unused, freeing messageevent');\r
+    athreaddata.messageevent.free;\r
+    //writeln('freeing thread data object');\r
+    athreaddata.free;\r
+    //writeln('deleting thread data object from hashtable');\r
+    deltree(@threaddata,inttostr(taddrint(athreaddata.threadid)));\r
+    //writeln('finished deleting thread data');\r
+  end else begin\r
+    //writeln('thread data is not unused');\r
+  end;\r
+end;\r
+\r
+function getwindowlongptr(ahwnd:hwnd;nindex:integer) : taddrint;\r
+var\r
+  window : twindow;\r
+begin\r
+  structurelock.acquire;\r
+  try\r
+    window := findtree(@windows,inttostr(ahwnd));\r
+    if window <> nil then begin\r
+      result := paddrint(taddrint(window.extrawindowmemory)+nindex)^;\r
+    end else begin\r
+      result := 0;\r
+    end;\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+end;\r
+\r
+function setwindowlongptr(ahwnd:hwnd;nindex:integer;dwNewLong : taddrint) : taddrint;\r
+var\r
+  window : twindow;\r
+begin\r
+  structurelock.acquire;\r
+  try\r
+    window := findtree(@windows,inttostr(ahwnd));\r
+    if window <> nil then begin\r
+      result := paddrint(taddrint(window.extrawindowmemory)+nindex)^;\r
+      paddrint(taddrint(window.extrawindowmemory)+nindex)^ := dwnewlong;\r
+    end else begin\r
+      result := 0;\r
+    end;\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+\r
+end;\r
+\r
+\r
+function DefWindowProc(ahWnd:HWND; auMsg:Integer; awParam:WPARAM; alParam:LPARAM):Integer; stdcall;\r
+begin\r
+  result := 0;\r
+end;\r
+\r
+function strdup(s:pchar) : pchar;\r
+begin\r
+  //swriteln('in strdup, about to allocate memory');\r
+  result := getmem(strlen(s)+1);\r
+  //swriteln('about to copy string');\r
+  strcopy(s,result);\r
+  //swriteln('leaving strdup');\r
+end;\r
+\r
+function RegisterClass(const lpWndClass:TWNDCLASS):ATOM;\r
+var\r
+  storedwindowclass:pwndclass;\r
+begin\r
+  structurelock.acquire;\r
+  try\r
+    //swriteln('in registerclass, about to check for duplicate window class');\r
+    storedwindowclass := findtree(@windowclasses, lpwndclass.lpszclassname);\r
+    if storedwindowclass <> nil then begin\r
+\r
+      if comparebyte(storedwindowclass^,lpwndclass,sizeof(twndclass)-sizeof(pchar)-sizeof(pchar)) <> 0 then begin\r
+        //swriteln('duplicate window class registered with different settings');\r
+        raise exception.create('duplicate window class registered with different settings');\r
+      end else begin\r
+        //swriteln('duplicate window class registered with same settings, tolerated');\r
+      end;\r
+    end else begin\r
+      //swriteln('about to allocate memory for new windowclass');\r
+      storedwindowclass := getmem(sizeof(twndclass));\r
+      //swriteln('about to copy windowclass from parameter');\r
+      move(lpwndclass,storedwindowclass^,sizeof(twndclass));\r
+      //swriteln('about to copy strings');\r
+      if lpwndclass.lpszmenuname <> nil then storedwindowclass.lpszmenuname := strdup(lpwndclass.lpszmenuname);\r
+      if lpwndclass.lpszclassname <> nil then storedwindowclass.lpszclassname := strdup(lpwndclass.lpszclassname);\r
+      //swriteln('about to add result to list of windowclasses');\r
+      addtree(@windowclasses,lpwndclass.lpszclassname,storedwindowclass);\r
+    end;\r
+    //swriteln('about to return result');\r
+    result := storedwindowclass;\r
+    //swriteln('leaving registerclass');\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+end;\r
+\r
+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;\r
+var\r
+  wndclass : pwndclass;\r
+  tm : tthreadmanager;\r
+  window : twindow;\r
+begin\r
+  structurelock.acquire;\r
+  try\r
+    window := twindow.create;\r
+    window.hwnd := nextwindowhandle;\r
+    result := window.hwnd;\r
+    nextwindowhandle := nextwindowhandle + 1;\r
+    addtree(@windows,inttostr(window.hwnd),window);\r
+    wndclass := findtree(@windowclasses,lpclassname);\r
+    window.extrawindowmemory := getmem(wndclass.cbwndextra);\r
+\r
+    getthreadmanager(tm);\r
+    window.threadid := tm.GetCurrentThreadId;\r
+    window.windowproc := wndclass.lpfnwndproc;\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+end;\r
+function DestroyWindow(ahWnd:HWND):WINBOOL;\r
+var\r
+  window : twindow;\r
+  windowthreaddata : tthreaddata;\r
+  currentmessage : tmessageintransit;\r
+  prevmessage : tmessageintransit;\r
+begin\r
+  //writeln('started to destroy window');\r
+  structurelock.acquire;\r
+  try\r
+    window := twindow(findtree(@windows,inttostr(ahwnd)));\r
+    if window <> nil then begin\r
+      freemem(window.extrawindowmemory);\r
+      //writeln('about to delete window from windows structure');\r
+      deltree(@windows,inttostr(ahwnd));\r
+      //writeln('deleted window from windows structure');\r
+      windowthreaddata := tthreaddata(findtree(@threaddata,inttostr(taddrint(window.threadid))));\r
+\r
+      if windowthreaddata <> nil then begin\r
+        //writeln('found thread data scanning for messages to clean up');\r
+        currentmessage := windowthreaddata.messagequeue;\r
+        prevmessage := nil;\r
+        while currentmessage <> nil do begin\r
+          while (currentmessage <> nil) and (currentmessage.msg.hwnd = ahwnd) do begin\r
+            if prevmessage = nil then begin\r
+              windowthreaddata.messagequeue := currentmessage.next;\r
+            end else begin\r
+              prevmessage.next := currentmessage.next;\r
+            end;\r
+            currentmessage.free;\r
+            if prevmessage = nil then begin\r
+              currentmessage := windowthreaddata.messagequeue;\r
+            end else begin\r
+              currentmessage := prevmessage.next;\r
+            end;\r
+          end;\r
+          if currentmessage <> nil then begin\r
+            prevmessage := currentmessage;\r
+            currentmessage := currentmessage.next;\r
+          end;\r
+        end;\r
+        //writeln('deleting thread data structure if it is unused');\r
+        deletethreaddataifunused(windowthreaddata);\r
+      end else begin\r
+        //writeln('there is no thread data to search for messages to cleanup');\r
+      end;\r
+      //writeln('freeing window');\r
+      window.free;\r
+      result := true;\r
+    end else begin\r
+      result := false;\r
+    end;\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+  //writeln('window destroyed');\r
+end;\r
+\r
+\r
+\r
+function PostMessage(hWnd:HWND; Msg:UINT; wParam:WPARAM; lParam:LPARAM):WINBOOL;\r
+var\r
+  threaddata : tthreaddata;\r
+  message : tmessageintransit;\r
+  messagequeueend : tmessageintransit;\r
+  window : twindow;\r
+begin\r
+  structurelock.acquire;\r
+  try\r
+    window := findtree(@windows,inttostr(hwnd));\r
+    if window <> nil then begin\r
+      threaddata := findthreaddata(window.threadid);\r
+      message := tmessageintransit.create;\r
+      message.msg.hwnd := hwnd;\r
+      message.msg.message := msg;\r
+      message.msg.wparam := wparam;\r
+      message.msg.lparam := lparam;\r
+      if threaddata.lcorethread then begin\r
+        //swriteln('posting message to lcore thread');\r
+        fdwrite(lcorelinkpipesend,message,sizeof(message));\r
+      end else begin\r
+        //writeln('posting message to non lcore thread');\r
+        if threaddata.messagequeue = nil then begin\r
+          threaddata.messagequeue := message;\r
+        end else begin\r
+          messagequeueend := threaddata.messagequeue;\r
+          while messagequeueend.next <> nil do begin\r
+            messagequeueend := messagequeueend.next;\r
+          end;\r
+          messagequeueend.next := message;\r
+        end;\r
+\r
+        //writeln('message added to queue');\r
+        if threaddata.waiting then threaddata.messageevent.setevent;\r
+      end;\r
+      result := true;\r
+    end else begin\r
+      result := false;\r
+    end;\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+\r
+end;\r
+\r
+function gettickcount : dword;\r
+var\r
+  result64: integer;\r
+  tv : ttimeval;\r
+begin\r
+  gettimeofday(tv);\r
+  result64 := (tv.tv_sec*1000)+(tv.tv_usec div 1000);\r
+  result := result64;\r
+end;\r
+\r
+function DispatchMessage(const lpMsg: TMsg): Longint;\r
+var\r
+  timerproc : ttimerproc;\r
+  window : twindow;\r
+  windowproc : twndproc;\r
+begin\r
+  ////swriteln('in dispatchmessage, msg.hwnd='+inttohex(taddrint(lpmsg.hwnd),16));\r
+  if (lpmsg.lparam <> 0) and (lpmsg.message = WM_TIMER) then begin\r
+    timerproc := ttimerproc(lpmsg.lparam);\r
+    timerproc(lpmsg.hwnd,lpmsg.message,lpmsg.wparam,gettickcount);\r
+    result := 0;\r
+  end else begin\r
+    structurelock.acquire;\r
+    try\r
+      window := findtree(@windows,inttostr(lpmsg.hwnd));\r
+      //we have to get the window procedure while the structurelock\r
+      //is still held as the window could be destroyed from another thread\r
+      //otherwise.\r
+      if window <> nil then begin\r
+        windowproc := window.windowproc;\r
+      end else begin\r
+        windowproc := nil;\r
+      end;\r
+    finally\r
+      structurelock.release;\r
+    end;\r
+    if assigned(windowproc) then begin\r
+      result := windowproc(lpmsg.hwnd,lpmsg.message,lpmsg.wparam,lpmsg.lparam);\r
+    end else begin\r
+      result := -1;\r
+    end;\r
+  end;\r
+end;\r
+\r
+procedure processtimers;\r
+begin\r
+end;\r
+\r
+function GetMessageinternal(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT; wremovemsg : UINT;peek:boolean): WINBOOL;\r
+var\r
+  tm : tthreadmanager;\r
+  threaddata : tthreaddata;\r
+  message : tmessageintransit;\r
+  nowtv : ttimeval;\r
+  timeouttv : ttimeval;\r
+  timeoutms : int64;\r
+\r
+begin\r
+  if hwnd <> 0 then raise exception.create('getting messages for an individual window is not supported');\r
+  if (wmsgfiltermin <> 0) or (wmsgfiltermax <> 0) then raise exception.create('message filtering is not supported');\r
+  structurelock.acquire;\r
+  result := true;\r
+  try\r
+    getthreadmanager(tm);\r
+    threaddata := findthreaddata(tm.GetCurrentThreadId);\r
+    if threaddata.lcorethread then raise exception.create('get/peek message cannot be used in the lcore thread');\r
+    message := threaddata.messagequeue;\r
+    gettimeofday(nowtv);\r
+    while (not peek) and (message=nil) and (not tv_compare(nowtv,threaddata.nexttimer)) do begin\r
+      threaddata.waiting := true;\r
+      structurelock.release;\r
+      if (threaddata.nexttimer.tv_sec = TV_invalidtimebig.tv_sec) and (threaddata.nexttimer.tv_usec = TV_invalidtimebig.tv_usec) then begin\r
+        threaddata.messageevent.waitfor(INFINITE);\r
+      end else begin\r
+\r
+        timeouttv := threaddata.nexttimer;\r
+        timeoutms := (timeouttv.tv_sec * 1000)+(timeouttv.tv_usec div 1000);\r
+        //i'm assuming the timeout is in milliseconds\r
+        if (timeoutms > maxlongint) then timeoutms := maxlongint;\r
+        threaddata.messageevent.waitfor(timeoutms);\r
+\r
+      end;\r
+      structurelock.acquire;\r
+      threaddata.waiting := false;\r
+      message := threaddata.messagequeue;\r
+      gettimeofday(nowtv);\r
+    end;\r
+    if (message=nil) and tv_compare(nowtv,threaddata.nexttimer) then begin\r
+      processtimers;\r
+    end;\r
+    message := threaddata.messagequeue;\r
+    if message <> nil then begin\r
+      lpmsg := message.msg;\r
+      if wremovemsg=PM_REMOVE then begin\r
+        threaddata.messagequeue := message.next;\r
+        message.free;\r
+      end;\r
+    end else begin\r
+      result :=false;\r
+    end;\r
+    deletethreaddataifunused(threaddata);\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+end;\r
+\r
+function GetMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax: UINT): WINBOOL;\r
+begin\r
+  result := getmessageinternal(lpmsg,hwnd,wmsgfiltermin,wmsgfiltermax,PM_REMOVE,false);\r
+end;\r
+\r
+function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): WINBOOL;\r
+begin\r
+  result := getmessageinternal(lpmsg,hwnd,wmsgfiltermin,wmsgfiltermax,wRemoveMsg,true);\r
+end;\r
+\r
+function SetEvent(hEvent:THevent):WINBOOL;\r
+begin\r
+  hevent.setevent;\r
+  result := true;\r
+end;\r
+\r
+function CreateEvent(lpEventAttributes:PSECURITYATTRIBUTES; bManualReset:WINBOOL; bInitialState:WINBOOL; lpName:pchar):tHevent;\r
+begin\r
+  result := teventobject.create(lpeventattributes,bmanualreset,binitialstate,lpname);\r
+end;\r
+\r
+function terminatethread(threadhandle:tthreadid;dummy : integer) : boolean;\r
+var\r
+  tm : tthreadmanager;\r
+begin\r
+  getthreadmanager(tm);\r
+  tm.killthread(threadhandle);\r
+  result := true;\r
+end;\r
+\r
+function waitforsingleevent(event:thevent;timeout:cardinal) : twaitresult;\r
+begin\r
+  result := event.waitfor(timeout);\r
+end;\r
+\r
+procedure removefrombuffer(n : integer; var buffer:string);\r
+begin\r
+  if n=length(buffer) then begin\r
+    buffer := '';\r
+  end else begin\r
+    uniquestring(buffer);\r
+    move(buffer[n+1],buffer[1],length(buffer)-n);\r
+    setlength(buffer,length(buffer)-n);\r
+  end;\r
+end;\r
+\r
+type\r
+  tsc=class\r
+    procedure available(sender:tobject;error:word);\r
+  end;\r
+\r
+var\r
+  recvbuf : string;\r
+\r
+procedure tsc.available(sender:tobject;error:word);\r
+var\r
+  message : tmessageintransit;\r
+  messagebytes : array[1..sizeof(tmessageintransit)] of char absolute  message;\r
+  i : integer;\r
+begin\r
+  //swriteln('received data on lcorelinkpipe');\r
+  recvbuf := recvbuf + lcorelinkpiperecv.receivestr;\r
+  while length(recvbuf) >= sizeof(tmessageintransit) do begin\r
+    for i := 1 to sizeof(tmessageintransit) do begin\r
+      messagebytes[i] := recvbuf[i];\r
+    end;\r
+    dispatchmessage(message.msg);\r
+    message.free;\r
+    removefrombuffer(sizeof(tmessageintransit),recvbuf);\r
+  end;\r
+end;\r
+\r
+procedure init;\r
+var\r
+  tm : tthreadmanager;\r
+  threaddata : tthreaddata;\r
+  pipeends : tfildes;\r
+  sc : tsc;\r
+begin\r
+  structurelock := tcriticalsection.create;\r
+  getthreadmanager(tm);\r
+  threaddata := findthreaddata(tm.GetCurrentThreadId);\r
+  threaddata.lcorethread := true;\r
+  fppipe(pipeends);\r
+  lcorelinkpipesend := pipeends[1];\r
+  lcorelinkpiperecv := tlasio.create(nil);\r
+  lcorelinkpiperecv.dup(pipeends[0]);\r
+  lcorelinkpiperecv.ondataavailable := sc.available;\r
+  recvbuf := '';\r
+end;\r
+\r
+var\r
+  lcorethreadtimers : thashtable;\r
+type\r
+  tltimerformsg = class(tltimer)\r
+  public\r
+    hwnd : hwnd;\r
+    id : taddrint;\r
+    procedure timer(sender : tobject);\r
+  end;\r
+\r
+procedure tltimerformsg.timer(sender : tobject);\r
+var\r
+  msg : tmsg;\r
+begin\r
+  ////swriteln('in tltimerformsg.timer');\r
+  fillchar(msg,sizeof(msg),0);\r
+  msg.message := WM_TIMER;\r
+  msg.hwnd := hwnd;\r
+  msg.wparam := ID;\r
+  msg.lparam := 0;\r
+  dispatchmessage(msg);\r
+end;\r
+\r
+function SetTimer(ahWnd:HWND; nIDEvent:taddrint; uElapse:UINT; lpTimerFunc:tTIMERPROC):UINT;\r
+var\r
+  threaddata : tthreaddata;\r
+  ltimer : tltimerformsg;\r
+  tm : tthreadmanager;\r
+  window : twindow;\r
+begin\r
+  structurelock.acquire;\r
+  try\r
+    window := findtree(@windows,inttostr(ahwnd));\r
+    if window= nil then raise exception.create('invalid window');\r
+    threaddata := findthreaddata(window.threadid);\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+  if threaddata.lcorethread then begin\r
+    getthreadmanager(tm);\r
+    if tm.GetCurrentThreadId <> window.threadid then raise exception.create('timers on the lcore thread may only be added and removed from the lcore thread');\r
+    if ahwnd = 0 then raise exception.create('timers on the lcore thread must be associated with a window handle');\r
+    if @lptimerfunc <> nil then raise exception.create('seperate timer functions are not supported');\r
+\r
+    //remove preexisting timer with same ID\r
+    killtimer(ahwnd,nIDEvent);\r
+\r
+    ltimer := tltimerformsg.create(nil);\r
+    ltimer.interval := uelapse;\r
+    ltimer.id := nidevent;\r
+    ltimer.hwnd := ahwnd;\r
+    ltimer.enabled := true;\r
+    ltimer.ontimer := ltimer.timer;\r
+\r
+    addtree(@lcorethreadtimers,inttostr(taddrint(ahwnd))+' '+inttostr(nIDEvent),ltimer);\r
+\r
+    result := nidevent;\r
+  end else begin\r
+    raise exception.create('settimer not implemented for threads other than the lcore thread');\r
+  end;\r
+end;\r
+\r
+function KillTimer(ahWnd:HWND; uIDEvent:taddrint):WINBOOL;\r
+var\r
+  threaddata : tthreaddata;\r
+  ltimer : tltimerformsg;\r
+  tm : tthreadmanager;\r
+  window : twindow;\r
+begin\r
+  structurelock.acquire;\r
+  try\r
+    window := findtree(@windows,inttostr(ahwnd));\r
+    if window= nil then raise exception.create('invalid window');\r
+    threaddata := findthreaddata(window.threadid);\r
+  finally\r
+    structurelock.release;\r
+  end;\r
+  if threaddata.lcorethread then begin\r
+    getthreadmanager(tm);\r
+    if tm.GetCurrentThreadId <> window.threadid then raise exception.create('timers on the lcore thread may only be added and remove from the lcore thread');\r
+    if ahwnd = 0 then raise exception.create('timers on the lcore thread must be associated with a window handle');\r
+    ltimer := tltimerformsg(findtree(@lcorethreadtimers,inttostr(taddrint(ahwnd))+' '+inttostr(uIDEvent)));\r
+    if ltimer <> nil then begin\r
+      deltree(@lcorethreadtimers,inttostr(taddrint(ahwnd))+' '+inttostr(uIDEvent));\r
+      ltimer.free;\r
+      result := true;\r
+    end else begin\r
+      result := false;\r
+    end;\r
+  end else begin\r
+    raise exception.create('settimer not implemented for threads other than the lcore thread');\r
+  end;\r
+end;\r
+\r
+end.\r