Re: BREAKing into Euphoria
- Posted by abuaf Feb 08, 2022
- 1739 views
An update. Dont mind the cd lib stuff. Shows usage of iupmglplot. Also shows how to drastically reduce IUP message polling CPU by reduced polling during idleness.
with define DEBUG_TYPE --popup debugger after type check failures otherwise comment out for the normal crash include euphoria/debug/debug.e include std/math.e include std/console.e include std/os.e --include dll.e --include machine.e include gui/iup/iup.e include gui/iup/iup_config.e include gui/iup/iup_mglplot.e /* include gui/iup/cd.e include gui/iup/cdiup.e */ constant user32 = open_dll( "user32" ) constant xSetForegroundWindow = define_c_func( user32, "SetForegroundWindow", {C_ULONG}, C_UINT ) -- BOOL SetForegroundWindow(HWND) constant xGetForegroundWindow = define_c_func( user32, "GetForegroundWindow", {}, C_UINT ) -- HWND GetForegroundWindow(void) atom focusOld=0 --start with NULL (invalid) atom debugTime=0 procedure traceOn(integer val) poke4(0x5b1a20, val) --ie TraceOn=val poke4(0x5b1440, 1) --color_trace = TRUE; --ie ensures the debugger pops up in color (not static uninit value of 0 ie B/W) end procedure global procedure goDebug(integer focusDebug) --go to the debugger at the next trac'able line focusOld=c_func(xGetForegroundWindow) --save the current focus'ed win handle debugTime=time() if focusDebug then c_func(xSetForegroundWindow, {hCon} ) else -- don't want to set focus to debugger; ie waiting for some iup gui user activity to enter trace'able code, so end if --switch to (debugger) console window traceOn(1) --TraceOn=1 ie trace out to the debugger at next "with trace" code. end procedure global procedure tr(object condition={}, sequence errorMessage={}) --atom condition <> 0, then print errorMessage, else ignore. Eg tr(1>0, {"%s %d", "five=", 5}) --string 'condition' for simple error message if atom(condition) and condition = 0 then return end if --failed condition if its numeric if length(errorMessage) >= 2 and sequence(errorMessage[1]) then --formatting string, args ... errorMessage=sprintf(errorMessage[1], errorMessage[2..$]) end if if atom(condition) then condition=sprintf("%d", condition) elsif length(condition) then errorMessage=sprintf("%s %s", {condition, errorMessage}) condition="" end if if length(errorMessage) or length(condition) then --non-empty string, so elaborate error printf(1, "\ntr(%s): %s\n", {condition, errorMessage}) end if goDebug(1) end procedure atom breakTime=0 function breakFunc(integer event) puts(1, "Break pressed... ") breakTime -= time() if breakTime > -1 then traceOn(0) --cancel trace out to debugger (set first press), so terminate immediately puts(1, "quick break repeat; exiting app...\n") return 0 --Quickly repeated break, so act as if break not handled by this fn, ie pass on to next handler (eu terminates) end if breakTime = time() --update timestamp goDebug(1) return 1 --handled the control signal so return TRUE for no further action. end function without trace --we dont trace inside type checks ifdef DEBUG_TYPE then -- pinpoints the type error location with the debugger function typeEnd() goDebug(1) --popup the debugger at the next traceable line return 1 --no type abort; continue running end function elsedef function typeEnd() return 0 --abort with type check error end function end ifdef type Ihandle(atom x) if x>NULL then --ok positive if trunc(x) = x then return 1 end if --no fractional part so x ok so continue running end if printf(1, "type Ihandle: bad value %d\n", {x}) return typeEnd() end type type cdCanvas(atom x) if x>NULL then --ok positive if trunc(x) = x then return 1 end if --no fractional part so x ok so continue running end if printf(1, "type cdCanvas: bad value %d\n", {x}) return typeEnd() end type type cdContext(atom x) if x>NULL then --ok positive if trunc(x) = x then return 1 end if --no fractional part so x ok so continue running end if printf(1, "type cdContext: bad value %d\n", {x}) return typeEnd() end type with trace Ihandle plot function k_any( Ihandle self, atom c ) tr(c='q', {"Debugging IUP client callback, c=%d", c}) return IUP_DEFAULT -- return IUP_IGNORE end function function action( Ihandle self, integer c, atom after ) if (c) then end if return K_asterisk end function --atom kernel32, xSetConsoleCtrlHandler, cbHR atom xSetConsoleCtrlHandler, cbHR procedure noIup() integer key while 1 do puts(1,"sleeping press q to quit, break to debug\n") sleep(2) key=get_key() printf(1,"awake, key=%d\n", {key}) if key = 'q' then exit end if end while end procedure function iupStart( object argc, object argv ) IupOpen( argc, argv ) IupMglPlotOpen() --init IupMGLPlot library plot = IupMglPlot() --creates a empty initial data set IupMglPlotBegin(plot,2) for i=1 to 70 do IupMglPlotAdd2D(plot, i, i*i) end for {}=IupMglPlotEnd(plot) --puts the plot data into _plot. Now we can use IupSetAttribute() to modify plot data -- IupSetAttribute(text, "SIZE", "200x") -- IupSetCallback(text, "ACTION", Icallback("action")) -- IupSetCallback(text, "K_ANY", Icallback("k_any")) --IupSetAttribute(plot, "AXS_X", "NO") --ok --IupSetAttribute(plot, "LEGEND", "YES") --ok --IupSetAttribute(plot, "GRID", "XY") --ok IupSetAttribute(plot, "DS_COLOR", "255 0 255") --ok "R G B" IupSetAttribute(plot, "DS_MODE", "BAR") --ok IupSetAttribute(plot, "REDRAW", NULL) /* Ihandle iCanvas = IupCanvas() --Ihandle *cnvs -- cdcanvas = cdCreateCanvas( CD_IUP, cnvs ); --static cdCanvas *cdcanvas . H:\dl\graphics\CanvasDraw\v5.9\cd-5.9_Sources.zip;cd\include\cd.h; cdCreateCanvas(cdContext *context, void *data); cdContext ctx=CD_IUP() cdCanvas canvas = cdCreateCanvas(ctx, iCanvas ) --static cdCanvas *cdcanvas . H:\dl\graphics\CanvasDraw\v5.9\cd-5.9_Sources.zip;cd\include\cd.h; cdCreateCanvas(cdContext *context, void *data); cdCanvasForeground(canvas, CD_BLUE) -- cdCanvasLineWidth(canvas, 3); -- cdCanvasLineStyle(canvas, CD_CONTINUOUS); cdCanvasRect(canvas, 0, 200, 0, 200) -- cdKillCanvas(canvas) -- IupMglPlotNewDataSet(plot, 2) --create another data set */ Ihandle dlg = IupDialog(plot) --create a dialogue element to put the plot into IupSetAttributes(dlg, "RASTERSIZE=640x480") IupSetAttribute(dlg, "TITLE", "temperature/moisture") -- IupSetAttribute(plot, "GRID=YES, MARGINBOTTOM=80, AXS_XTICKROTATENUMBER=YES, MARGINRIGHT=80") -- IupSetAttribute(plot, "READONLY", "NO") -- IupSetAttribute(dlg, "MARGIN", "100x100") IupShowXY(dlg, IUP_CENTER, IUP_CENTER) return EXIT_SUCCESS end function without trace --we dont want to debug iupIdle or the iup event polling loop atom idleSleep=0 function iupIdle() idleSleep=.11 --iup is idle so increase the polling delay return IUP_DEFAULT end function constant kernel32 = open_dll( "kernel32.dll" ) constant xGetConsoleWindow = define_c_func( kernel32, "GetConsoleWindow", {}, C_ULONG ) atom hCon = c_func( xGetConsoleWindow, {} ), hIup=0, hCurrent=0 constant xGetNumberOfConsoleInputEvents = define_c_func( kernel32, "GetNumberOfConsoleInputEvents", {C_ULONG , C_POINTER}, C_INT) --HANDLE hConsoleInput, _Out_ LPDWORD lpNumberOfEventsRead constant dwordVar = allocate(4) ------------------ CONSOLE INPUT FNs integer xReadConsoleInput = define_c_func( kernel32, "ReadConsoleInputW", {C_ULONG , C_ULONG, C_INT, C_POINTER}, C_INT) --HANDLE hConsoleInput, _Out_ PINPUT_RECORD lpBuffer, _In_ DWORD nLength, _Out_ LPDWORD lpNumberOfEventsRead if xReadConsoleInput = -1 then -- Using "ReadConsoleInput" doesnt work, so we try this for the failed explicit "ReadConsoleInputW" xReadConsoleInput = define_c_func( kernel32, "ReadConsoleInputA", {C_ULONG , C_ULONG, C_INT, C_POINTER}, C_INT) end if constant ptrInputRecord = allocate(24) --mem block for a single INPUT_RECORD constant KEY_EVENT=1 --EventType is a key press/release constant MENU_EVENT=8 constant FOCUS_EVENT=16 -- INPUT_RECORD typedef; abs address of record members in ptrInputRecord (ie includes +offset) constant EventType = ptrInputRecord+0 -- KEY_EVENT union; constant bKeyDown=ptrInputRecord+4 constant wRepeatCount=ptrInputRecord+8 constant wVirtualKeyCode=ptrInputRecord+10 constant wVirtualScanCode=ptrInputRecord+12 constant AsciiChar=ptrInputRecord+14 constant dwControlKeyState=ptrInputRecord+16 ------------------ constant xGetStdHandle= define_c_func( kernel32, "GetStdHandle", {C_INT}, C_ULONG) constant hConsoleInput=c_func(xGetStdHandle, {-10} ) -- -10 = STD_INPUT_HANDLE if hConsoleInput = -1 then ?9/0 end if --BOOL SetConsoleTitle(char *lpConsoleTitle) --Kernel32 -- see H:\dl\c\win32\console API.pdf EVENT_CONSOLE_CARET callback event . -- H:\dl\c\win32\WinEvents API.pdf p608 sequence cmd = command_line() sequence argv = cmd[3..$] integer argc = length( argv ), iuret xSetConsoleCtrlHandler = define_c_func(kernel32,"SetConsoleCtrlHandler", {C_POINTER, -- PHANDLER_ROUTINE pHandlerRoutine, // address of handler function C_INT}, -- BOOL fAdd // handler to add or remove C_INT) -- BOOL cbHR = call_back(routine_id("breakFunc")) --{’+’, id} for cdecl if not c_func(xSetConsoleCtrlHandler,{cbHR,1}) then ?9/0 end if --noIup() --test interrupting a console (without iup) loop with async break key. iupStart( argc, argv ) IupSetGlobalFunction("IDLE_ACTION", Icallback("iupIdle")) --iup will call this idle callback so we can flag when iup is idle and reduce polling freq -- IupMainLoop() --blocks this foreground process until user closes GUI loop do --iup event polling loop printf(1, "Press break to debug ") ? idleSleep & hCon & hIup & hCurrent & date() sleep(idleSleep) ----- peek into the keyboard queue for debugger keyboard code sent from external app (NB: we dont use get_key() because onTrace=1 which will cause long delays until the next tracable line occurs and the debugger starts) if not c_func(xGetNumberOfConsoleInputEvents, {hConsoleInput, dwordVar} ) then ?9/0 end if for i = 1 to peek4s(dwordVar) do --read each event (if > 0) {}=getKey() end for ----- hCurrent=c_func(xGetForegroundWindow) if hCurrent != hCon then hIup=hCurrent end if if focusOld then --old window focus before debugger invoked if focusOld = hCon then focusOld=hIup end if --debugging IUP window, so reject hCon if focusOld != hCurrent and time()-debugTime > 0.3 then --old windows not focussed and been some time (stuck in the debugger outside this block) since debugTime updated. {}=c_func(xSetForegroundWindow, {focusOld} ) --focus the old windows focusOld =0 --and finnished refocusing old window, so invalidate (NULL) end if debugTime=time() --update time last in this block end if idleSleep=.01 --do a short sleep for good user interaction speed, unless iup becomes idle and calls iupIdle() which will set a longer sleep to reduce CPU wastage --IupSetAttribute(plot, "REDRAW", NULL) until IupLoopStep() = IUP_CLOSE -- poll and execute next iup event (if any). This will also call iupIdle() if iup gui event queue is idle end loop IupClose() function getKey() --get_key() replacement, that doesnt pause if not c_func(xReadConsoleInput, {hConsoleInput , ptrInputRecord, 1, dwordVar}) then ?9/0 end if --read 1 INPUT_RECORD event, NULL to store # events read. -- *dwordVar stores the address of the win32 provided input record buffer if peek2u(EventType) != KEY_EVENT or not peek(bKeyDown) then ----ignore non-key events or key up events return -1 end if integer ch=peek(AsciiChar) if ch = 3 then --external global 'Alt-Break' Autohotkey script attached to our console and sent break key (useful since we dont loose gui focus) goDebug(1) elsif ch = 0 then --a control key return -1 --ignore end if -- ? ch return ch end function