I recently came across an article of Manfred Mahlow from four years back which came close to solving a problem I am having. The article, "Using Glade to Create GTK+ Applications in Forth" touched upon signal handlers, stating that Forth could not be used, but only C to create them. I think my problem remains a small one, but I am stumped, I wold be very appreciative of any suggestions. Secondarily, if anyone knows how to send an email to Mr. Mahlow without using a dash in the address, I would love to hear it.
I am writing an application using the Linux version of SwiftForth to create a custom window and receive keypresses and write to that window. My goal is to implement Charles Moore's colorForth, enhanced and more user friendly. I decided to use GTK+, knowing little about it, but I have been successful but for the callbacks (signal handlers). There are only two pages of code in the whole thing, so I put it here:
\ ------------------------ COLOR FORTH -----------------------
ANEW TASK-COLORFORTH
LIBRARY /usr/lib/i386-linux-gnu/libgtk-3.so.0.1000.8
FUNCTION: gtk_window_new ( code -- addr )
FUNCTION: gtk_widget_destroy ( wptr -- )
FUNCTION: gtk_window_close ( wptr -- )
FUNCTION: gtk_widget_show ( wptr -- )
FUNCTION: gtk_init ( -- )
FUNCTION: gtk_window_set_decorated ( wptr flag -- )
FUNCTION: gtk_window_move ( wptr x y -- )
\ FUNCTION: gtk_widget_new ( n addr -- ) ( try 0 0 )
\ FUNCTION: gtk_widget_map ( wptr -- )
\ FUNCTION: gtk_widget_show_all ( wptr -- )
FUNCTION: gtk_window_get_screen ( wptr -- wgptr )
\ FUNCTION: gtk_window_present ( wptr -- )
FUNCTION: gtk_window_set_title ( wptr TitlePtr -- )
FUNCTION: gtk_window_resize ( wptr width height -- )
FUNCTION: gtk_widget_modify_bg ( wptr state cptr -- )
\ FUNCTION: gtk_widget_modify_fg ( wptr state cptr -- )
FUNCTION: gtk_widget_modify_text ( wptr state cptr -- )
FUNCTION: gtk_widget_modify_base ( wptr statre cptr -- )
FUNCTION: g_signal_connect_data ( wptr name callback data 0 0 -- )
FUNCTION: gtk_main ( -- )
FUNCTION: gtk_main_quit ( -- )
FUNCTION: gtk_widget_set_events ( wptr flag -- )
LIBRARY /usr/lib/i386-linux-gnu/libgdk-3.so.0.1000.8
FUNCTION: gdk_event_get_keyval ( eptr kptr -- )
FUNCTION: gdk_event_get_state ( eptr sptr -- )
FUNCTION: gdk_event_get_event_type ( eptr -- type ) ( requires GET.RETURN )
\ LIBRARY /usr/lib/i386-linux-gnu/libgio-2.0.so.0.4000.0
\ LIBRARY /lib/i386-linux-gnu/libglib-2.0.so.0.4000.0
\ LIBRARY /usr/lib/i386-linux-gnu/libpango-1.0.so.0.3600.3
\ LIBRARY /usr/lib/i386-linux-gnu/libatk-1.0.so.0.21009.1
\ LIBRARY /usr/lib/i386-linux-gnu/libgobject-2.0.so.0.4000.0
\ LIBRARY /usr/lib/i386-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7
VARIABLE Window-Ptr
VARIABLE Event-Ptr
VARIABLE KeyVal
VARIABLE KeyState
Z" ColorForth" VALUE Title
Z" key_press_event" VALUE Keypress-Name
Z" delete_event" VALUE Delete-Event-Name
Z" destroy" VALUE Destroy-Name
CREATE Background 0 , 0 H, 0 H, $2000 H,
CREATE Black 0 , 0 H, 0 H, 0 H,
CREATE White 0 , $E000 H, $E000 H, $8000 H,
\ ----------------------------------------------------------------
ICODE LEAVE.FALSE
EAX EAX SUB
RET
END-CODE
ICODE GET.RETURN
4 # EBP SUB
EBX 0 [EBP] MOV
EAX EBX MOV
RET
END-CODE
\ This may be the part where I am having problems
: SHUTDOWN
Window-Ptr # DUP gtk_window_close
gtk_widget_destroy
;
: CB.DELETE.EVENT ( wptr data -- false to destroy )
RDROP RDROP ( assuming 2 paramaters on Rstack and return -1 )
LEAVE.FALSE
;
: CB.DESTROY ( wptr data -- )
RDROP RDROP ( two parameters here )
gtk_main_quit
;
: CB.KEYPRESS ( wptr eptr data -- ) ( assuming 3 parameters on Rstack )
Window-Ptr # . R> . R> . R> . R# ( This for diagnosis – eventually )
SHUTDOWN ( goes to a [SWITCH )
;
: STARTUP ( -- )
Window-Ptr OFF Event-Ptr OFF
gtk_init
0 gtk_window_new ?DUP ( 0 = GTK_WINDOW_TOPLEVEL )
IF DUP Window-Ptr !
DUP 1024 gtk_widget_set_events ( 1024 for Keypress signals )
DUP Title gtk_window_set_title
DUP 1280 850 gtk_window_resize
DUP 0 gtk_window_set_decorated
DUP 0 Background gtk_widget_modify_bg
DUP Delete-Event-Name [ ' CB.DELETE.EVENT +ORIGIN ] LITERAL 0
0 0 g_signal_connect_data
DUP Destroy-Name [ ' CB.DESTROY +ORIGIN ] LITERAL 0
0 0 g_signal_connect_data
DUP Keypress-Name [ ' CB.KEYPRESS +ORIGIN ] LITERAL 0
0 0 g_signal_connect_data
gtk_widget_show
THEN
gtk_main
;
I could not use the usual g_signal_connect because I could not find it in any library I have. If it turns up, I would use it since the code is simpler. However, this code above compiles, and when I type STARTUP, it does just that. The window is there as I have specified, the signals are set, but pressing any key crashes SwiftForth with a "segmentation fault".
As you see, I am feeding the absolute address of the handler to the signal connect function. I have also tried to put that address into a variable and handing it as a pointer, but this yields a GTK+ error.
In writing the handlers I assumed that it would operate like any library call. The input parameters would be on the return stack, supplied by GTK as the handler code executes, and I am supposed to put any returns into EAX. But this analysis could be wrong. I may be preparing the address wrong or handler"s parameters wrong, or have totally the wrong concept.
I am hoping that, despite the statement that handlers would have to be in C, there is some way to simulate what the C code would do, in assembler if necessary.
I sense that I am one hurdle away from making this breakthrough, and if anyone could send a few suggestions or places where I might look, I would be very grateful.
FUNCTION: g_signal_connect_data ( wptr name callback data 0 0 -- )
I see you're using things like ' CB.DELETE.EVENT +ORIGIN to pass as
the callback parameter.
Shouldn't you use a Forth callback to begin with? Your
CB.DELETE.EVENT (and the others) needs and expects a Forth
context.
In SwiftForth this can be achieved with
' CB.DELETE.EVENT 2 CB: *CB.DELETE>EVENT
Pass *CB.DELETE>EVENT as the callback parameter to g_signal_connect_data
CB: creates a wrapper around CB.DELETE.EVENT which establishes a
temporary Forth environment to run in. See the SwiftForth manual
for accessing the parameters given to callbacks. Of course the
other callbacks should be adapted likewise.
Note: SwiftForth deals with the inputs in a specific way. Other
Forth systems differ in how they handle the callback input
parameters. But in general it's good not to depend blindly on
the return stack. Always check the Forth documentation.
While the import-library words facilitate non-Forth functions to
run in a Forth environment, the reverse, running Forth words in
a foreign context, is done with the callback words. They allow
for signal handlers, completion handlers, exception handlers,
start routine for POSIX threads, method implementations for Objective-C
classes, etc., etc. Many Forth systems running under an OS like
SwiftForth, VFX, iForth, MacForth, iMops and Gforth provide ways
to create Forth callbacks. So any claim Forth can't be used here
is refuted I guess. See this Forth + GTK example.
BTW, I ran the adapted code in SwiftForth OS X. OK ;-)
Related
I am using Eclipse LDT for development, using the packaged Lua EE and Interpreter for Lua 5.2. I need to call the commandDatabase() method from my main method, though when I try, I receive the error:
"attempt to call global 'commandDatabase' (a nil value)".
I have looked up this error, and I am, as far as I can tell, defining methods in the right order.
Lua - attempt to call global 'contains' (a nil value)
When I view it in the debugger, the interpreter does not seem to find any methods I define between commandSystem and commandHelp. It shows each other function in the Variables area as e.g. ["commandSystem"] = function() but commandDatabase() does not appear
I have tried calling a wrapper method like so:
function commandDatabaseStep()
return commandDatabase()
end
... but this did not work either (same error)
The relevant code:
-- System command
function commandSystem()
...
end
-- Database command
function commandDatabase()
if arguments[2] == "no_arg1" then
print("The 'database' command must have at least one argument: [generate, wipe, dump, delete, get <system_name>]", true)
return 2
elseif arguments[2] == "generate" then
local file = io.open(database, "w+")
file:write("#ssmhub database")
file:close(file)
return 1
elseif arguments[2] == "dump" then
print("= DUMP START =")
for line in io.lines(database) do
print(line)
end
print("= DUMP END =")
return 1
-- 1+
elseif arguments[2] == "get" then
-- 2+
if isEmpty(arguments[3]) then
print("The 'database get' command must have a <name> parameter")
return 0
-- 2-
else -- 3+
local file = io.open(database, "r")
for line in io.lines(file) do -- 4+
local state = ""
local id = ""
local dividersFound = 0
line:gsub(".", function(c) -- 5+
if c == "|" then -- 6+
if dividersFound == 0 then -- 7+
state = state .. c
end -- 7-
if dividersFound == 1 then -- 8+
id = id .. c
end -- 8-
dividersFound = dividersFound + 1
end -- 6-
end) -- 5-
io.close(file)
end -- 4-
end -- 3-
else -- 9+
print("Illegal argument for command. Use 'help' for a list of commands and arguments.")
return 0
end -- 9-
end -- 2-
end -- 1-
function commandHelp()
...
end
-- Main
function main()
arguments = readProgramArguments()
commandArgument = arguments[1]
commandCompleteCode = 0
-- Process help and system commands
if commandArgument == "database" then
commandCompleteCode = commandDatabase()
end
end main()
As #luther pointed out, I had one-too-many end-s in commandDatabase.
This wasn't flagged in my IDE because I had not end-ed commandSystem, so commandSystem was nested inside of it.
To fix: add an end to commandSystem, and remove the end which I tagged '-- 1-'.
I have a script setup to run overnight on my Filemaker server, but every night it returns this error:
Schedule "Recheck All Flags" scripting error (201) at "MasterDatabase : Recheck All Flags : ### : Set Field
Where ### is just a random number, probably the line in the script. In the above example let's just say it was 398.
I can't copy and paste the script to here, but it is very simple. It's one big loop over all records in the database and it checks a bunch of If statements. It looks like:
If [MasterDatabase::Start date = ""]
Set Field [MasterDatabase::Flagged for Discrepancy; "Yes"]
Commit Records/Requests [With dialog:Off]
End If
Why might this be failing when running on the server overnight?
Make sure you tell the server to go to the correct layout before the script starts, when the server starts up the script it will do the work on the layout that it is configured for the DB to open by default.
You can do something like:
If [ $$PLATFORM = "Server" ]
(Go to Layout X)
End If
The global variable $$PLATFORM can be defined in your On First Window Open Script like so:
Case (
PatternCount ( Get ( ApplicationVersion ) ; "Server" ) ;
"Server" ;
PatternCount ( Get ( ApplicationVersion ) ; "Data API" ) ;
"Data API" ;
Get ( SystemPlatform ) = 4 ;
"Web" ;
Choose (
Get ( Device ) ;
"Desktop" ; // 0 - Unknown
"Desktop" ; // 1 - Mac
"Desktop" ; // 2 - PC
"Tablet" ; // 3 - Tablet
"Phone" // 4 - Phone
)
)
If you already have that covered then make sure whatever user you are using for the PSOS has access to that layout, records scripts and fields.
/* medical diagnostic system
start with ?- check.
*/
check:-
checkfor(Disease),
write('I believe you have '),
write(Disease),
nl,
undo.
/* disease to be checked */
checkfor(cold):- cold.
/* cold */
cold:-
checkSymptom(a),
checkSymptom(b),
checkSymptom(c),
nl.
askQuestion(Question):-
write('Do you have the Symptom '),
write(Question),
write('?'),
read(Reply),
nl,
( (Reply == yes ; Reply == y)
-> assert(yes(Question))
; assert(no(Question)), fail
).
:- dynamic yes/1,no/1.
checkSymptom(S) :-
( yes(S)
-> true
; ( no(S)
-> fail
; askQuestion(S)
)
).
undo :- retract(yes(_)), fail.
undo :- retract(no(_)), fail.
undo.
I want assertion that if in current cycle signal 'a' equal to "0110"(in binary) in the next cycle signal'b'not bigger than 31(it should be between 0 and 31.it should be less than 00000000000000000000000000011111)(its width equal 32)
Can everyone help me to write assertion?!
Excuse me for my bad english.
assert property ( # (posedge clk ) (a == 32'b0110) |=> ( b > 32'd0 && b < 32'd32 ) );
assert - will set the property( assertion ) into action. The property has to be based on a clock . Choose the appropriate clock which is triggering the registers a & b in the design. Implication operator |=> indicates that the property has to be true in the next clock cycle. In this case if a equals 6, the next cycle b has to between 0 and 32 ).
In case of a failure some similar message ( based on the simulator ) will be displayed.
top.unnamed$$_0: started at ns failed at ns
Offending '((b > 0) && (b < 32))'
You can read up a basic tutorial on assertions
https://www.doulos.com/knowhow/sysverilog/tutorial/assertions/
I need a program that can create pre-defined shapes on screen according to that commands I send to it via TCP.
I'm trying to listen to a port and so that I can use them. Before waiting of a command (via network) I have the commands required to create a square (I plan to change its attributes via network commands)
The problem is it is not creating any graphics or opening the window as it should be..
require "socket"
require "mime"
require "ltn12"
host = "localhost"
port = "8080"
server, error = socket.bind(host, port)
if not server then print("server: " .. tostring(error)) os.exit() end
screen=MOAISim.openWindow ( "test", 640, 640 )
viewport = MOAIViewport.new (screen)
viewport:setSize ( 640, 640 )
viewport:setScale ( 640, 640 )
layer = MOAILayer2D.new ()
layer:setViewport ( viewport )
MOAISim.pushRenderPass ( layer )
function fillSquare (x,y,radius,red,green,blue)
a = red/255
b = green/255
c = blue/255
MOAIGfxDevice.setPenColor ( a, b, c) -- green
MOAIGfxDevice.setPenWidth ( 2 )
MOAIDraw.fillCircle ( x, y, radius, 4 ) -- x,y,r,steps
end
function onDraw ( )
fillSquare(0,64,64, 0,0,255)
end
scriptDeck = MOAIScriptDeck.new ()
scriptDeck:setRect ( -64, -64, 64, 64 )
scriptDeck:setDrawCallback ( onDraw)
prop = MOAIProp2D.new ()
prop:setDeck ( scriptDeck )
layer:insertProp ( prop )
while 1 do
print("server: waiting for client command...")
control = server:accept()
command, error = control:receive()
print(command,error)
error = control:send("hi from Moai\n")
end
It is waiting of the command from client at control = server:accept() but it is not opening up the graphics window as it should.. Is there any command to force it to open or render
Thank you
MOAI doesn't run your scripts in a separate thread. A blocking call (server:accept) or forever loop (while true do) will block your MOAI app and it will appear to freeze while it merrily sits in your script forever.
So you have to do two things:
Use non-blocking calls. In this case, you need to set your server's timeout to 0. That makes server:accept return immediately. Check it's return value to see if you got a connection.
Put your while loop in a coroutine and yield once per iteration.
You'll need to handle the client the same way, using non-blocking calls in a coroutine loop.
function clientProc(client)
print('client connected:', client)
client:settimeout(0) -- make client socket reads non-blocking
while true do
local command, err = client:receive('*l')
if command then
print('received command:', command)
err = client:send("hi from Moai\n")
elseif err == 'closed' then
print('client disconnected:', client)
break
elseif err ~= 'timeout' then
print('error: ', err)
break
end
coroutine.yield()
end
client:close()
end
function serverProc()
print("server: waiting for client connections...")
server:settimeout(0) -- make server:accept call non-blocking
while true do
local client = server:accept()
if client then
MOAICoroutine.new():run(clientProc, client)
end
coroutine.yield()
end
end
MOAICoroutine.new():run(serverProc)
Set the timeout for the server socket, since the accept is a blocking call.
server:settimeout(1)
Thanks Mud...I found that before u replied so the following coroutine works
function threadFunc()
local action
while 1 do
stat, control = server:accept()
--print(control,stat)
while 1 do
if stat then
command, error = stat:receive()
print("Comm: ", command, error)
if command then
stat:close()
print("server: closing connection...")
break
else
break
end
--[[
error = stat:send("hi")
if error then
stat:close()
print("server: closing connection...",error)
break
end ]] --
else
break
end
end
coroutine.yield()
end
end
That was very helpful though