Author Topic: MTSB  (Read 9446 times)

JRS

  • Guest
MTSB
« on: December 24, 2010, 10:57:06 PM »
I have been trying to get my arms around what my limitations are using the sbhttpd ScriptBasic HTTP protocol server in a desktop environment. In essence what sbhttpd offers is a client / server relationship using a HTTP 1.0 protocol to interact with it's clients. (a browser or anything else that can open a socket) So what this means is the that app-server can respond with a HTML response (if running in a browser) or a Glade XML response. (if a desktop GUI app)

The following is an example of using scriba (ScriptBasic console interpreter) and the cURL extension module to run a script on the server.

mtsbcurl.sb
Code: [Select]
INCLUDE curl.bas

ch = curl::init()
curl::option ch,"URL","http://127.0.0.1:8080/mtsb/john.bas"
txt = curl::perform(ch)
PRINT txt
curl::finish ch

john.bas
Code: [Select]
PRINT "Status: 200\nContent-Type: text/html\n\n"
PRINT "Server Script\n"
FOR x = 1 TO 10
  PRINT x,"\n"
NEXT

jrs@Laptop:~/SB/test$ scriba mtsbcurl.sb
Server Script
1
2
3
4
5
6
7
8
9
10
jrs@Laptop:~/SB/test$

I see this as a potential for common ground between web and desktop application development with the goal of reusable code.
« Last Edit: December 25, 2010, 11:22:35 PM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #1 on: December 26, 2010, 05:23:24 PM »
If the goal is to use sbhttpd to provide the client / server layer for both web and desktop applications, then it all starts with laying out the cookies. (if Santa did bring you anything, you may have left out this step)  :D

I have been using sbhttpd as a proxy application server for dynamic web applications for years and couldn't be happier. I will show how you can use sbhttpd to support desktop applications using the same application server (local or remote) the web clients use. The first thing on a list is to establish a session with the server. This is done with a cookie that contains a session ID the server generated and is used during the life of the session. The server needs to know the session ID before it can access previously stored variables or setting new ones. The following example gets one script to establish the session ID and then gets the second script telling sbhttpd what it's session ID is and retrieves the session variable set in the prior script along with printing its current session ID.

mtsbcurl.sb
Code: [Select]
INCLUDE curl.bas

ch = curl::init()

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/svar.sb")
curl::option(ch,"COOKIEFILE","")
txt = curl::perform(ch)
PRINT txt,"\n"

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/svar.ovr")
curl::option(ch,"COOKIE","mtsb=" & CHOMP(txt) & ";")
txt = curl::perform(ch)
PRINT txt,"\n"

curl::finish(ch)

svar.sb - 1st web script
Code: [Select]
INCLUDE cgi.bas
INCLUDE mt.bas

session_id = mt::NewSessionId()
mt::SetSessionId(session_id)
mt::SetSessionVariable("myvar", "John")
cgi::Header(200, "text/html")
cgi::SetCookie("mtsb", session_id)
cgi::FinishHeader()
PRINT session_id

svar.ovr - 2nd web script
Code: [Select]
INCLUDE cgi.bas
INCLUDE mt.bas

session_id = cgi::Cookie("mtsb")
IF mt::CheckSessionId(session_id) THEN
  mt::SetSessionId(session_id)
  myvar = mt::GetSessionVariable("myvar")
  cgi::Header(200, "text/html")
  cgi::SetCookie("mtsb", session_id)
  cgi::FinishHeader()
  PRINT myvar,"\n"
  PRINT session_id,"\n"
ELSE
  cgi::Header(200, "text/html")
  cgi::FinishHeader()
  PRINT "Invalid Session ID\n"
END IF

Results:

jrs@Laptop:~/SB/test$ scriba mtsbcurl.sb
amfdpfdhpdcdihhofgceeoajkcapckjf
John
amfdpfdhpdcdihhofgceeoajkcapckjf

jrs@Laptop:~/SB/test$


Note:  Curl has an option to create a COOKIEJAR that automates the cookie processing on the client (scriba) side but the SB curl extension module doesn't support that option currently. (hence the reason I'm printing the session ID to send back to the client in the first script. If FireFox didin't store their cookies in a SQLite DB, I could have pointed COOKIEFILE to it.  >:(


« Last Edit: December 26, 2010, 06:30:43 PM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #2 on: December 26, 2010, 07:18:04 PM »
I lucked out and adding COOKIEJAR was a one line change and recompile of the curl.so extension module. I'm glad the automated cookie handling is working and now I can move on to working with a shared database and submitting client form data to the server.

SB curl interface.c
Code: [Select]
/*
Here you have to list all the CURL options that get char * parameter and are handles
transparently passed to CURL.

The macro is (re)defined
- in the string parameter declaration,
- initializing the parameters to NULL
- during parameter setting
- and release of memory when finish a curl handle.

That helps you to maintain new options because this is the only place where the name of
the new option is to be present.
*/
#define STRING_OPTIONS \
STRING_OPTION(PROXY)\
STRING_OPTION(URL)\
STRING_OPTION(USERPWD)\
STRING_OPTION(PROXYUSERPWD)\
STRING_OPTION(RANGE)\
STRING_OPTION(REFERER)\
STRING_OPTION(USERAGENT)\
STRING_OPTION(FTPPORT)\
STRING_OPTION(COOKIE)\
STRING_OPTION(SSLCERT)\
STRING_OPTION(SSLCERTPASSWD)\
STRING_OPTION(COOKIEFILE)\
STRING_OPTION(COOKIEJAR)\
STRING_OPTION(CUSTOMREQUEST)\
STRING_OPTION(INTERFACE)\
STRING_OPTION(KRB4LEVEL)\
STRING_OPTION(WRITEINFO)\
STRING_OPTION(CAINFO)\
STRING_OPTION(RANDOM_FILE)\
STRING_OPTION(EGDSOCKET)\

mtsbcurl.sb
Code: [Select]
INCLUDE curl.bas

ch = curl::init()

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/svar.sb")
curl::option(ch,"COOKIEJAR","/home/jrs/SB/test/cookie.txt")
txt = curl::perform(ch)
PRINT txt,"\n"

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/svar.ovr")
curl::option(ch,"COOKIE","mtsb")
txt = curl::perform(ch)
PRINT txt,"\n"

curl::finish(ch)
Note: The printing of the session_id in this new version is for visual (debugging) validation only.

cookie.txt
Code: [Select]
jrs@Laptop:~/SB/test$ scriba mtsbcurl.sb
fckbkipdoecbpekijkohgocnligejhlp
John
fckbkipdoecbpekijkohgocnligejhlp

jrs@Laptop:~/SB/test$ cat cookie.txt
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This file was generated by libcurl! Edit at your own risk.

127.0.0.1 FALSE /mtsb/ FALSE 0 mtsb fckbkipdoecbpekijkohgocnligejhlp
jrs@Laptop:~/SB/test$

svar.sb
Code: [Select]
NCLUDE cgi.bas
INCLUDE mt.bas

session_id = mt::NewSessionId()
mt::SetSessionId(session_id)
mt::SetSessionVariable("myvar", "John")
cgi::Header(200, "text/html")
cgi::SetCookie("mtsb", session_id)
cgi::FinishHeader()
PRINT session_id

svar.ovr
Code: [Select]
INCLUDE cgi.bas
INCLUDE mt.bas

session_id = cgi::Cookie("mtsb")
IF mt::CheckSessionId(session_id) THEN
  mt::SetSessionId(session_id)
  myvar = mt::GetSessionVariable("myvar")
  cgi::Header(200, "text/html")
  cgi::SetCookie("mtsb", session_id)
  cgi::FinishHeader()
  PRINT myvar,"\n"
  PRINT session_id,"\n"
ELSE
  cgi::Header(200, "text/html")
  cgi::FinishHeader()
  PRINT "Invalid Session ID\n"
END IF
« Last Edit: December 26, 2010, 09:04:15 PM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #3 on: December 27, 2010, 05:11:43 PM »
I thought I would start off with the browser (HTML) version first to show how the flow of a CGI client/server application might look. Keep in mind there is no formating (CSS) or validation to keep this simple and easy to read.

webcustomer.sb
Code: [Select]
' Program: webcustomer.sb
' Version: 1.0
'    Date: 2010-12-27
'      By: JRS

INCLUDE cgi.bas
INCLUDE mt.bas
INCLUDE mysql.bas

dbh = mysql::RealConnect("localhost","root","xxx","sbtest")

OPTION cgi$Method cgi::GET or cgi::POST

session_id = cgi::Cookie("webcust")

New_Session:

IF session_id = undef THEN
  session_id = mt::NewSessionId()
  mt::SetSessionId(session_id)
ELSE
  IF mt::CheckSessionId(session_id) = False THEN
    session_id = undef
    GOTO New_Session
  END IF
  mt::SetSessionId(session_id)
END IF

IF cgi::RequestMethod() = "GET" THEN GOTO Enter_Customer
cust_id = cgi::PostParam("cust_id")
cust_name = cgi::PostParam("cust_name")
cust_addr = cgi::PostParam("cust_addr")
cust_city = cgi::PostParam("cust_city")
cust_state = cgi::PostParam("cust_state")
cust_zip = cgi::PostParam("cust_zip")
cust_phone = cgi::PostParam("cust_phone")

SQL = "INSERT INTO customer (cust_id, cust_name, cust_addr, cust_city, cust_state, cust_zip, cust_phone) VALUES ('" & cust_id & "', '" & cust_name & "', '" & cust_addr & "', '" & cust_city & "', '" & cust_state & "', '" & cust_zip & "', '" & cust_phone & "')"   
mysql::query(dbh, SQL)

SQL = "SELECT * FROM customer"
mysql::query(dbh, SQL)

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()
PRINT """
<html>
<head>
<title>Customer Master File list</title>
</head>
<body>
<table border="1">
  <tr>
    <th>ID</th>
    <th>Name</th>
    <th>Address</th>
    <th>City</th>
    <th>State</th>
    <th>Zip</th>
    <th>Phone</th>
  </tr>
"""
WHILE mysql::FetchHash(dbh, dbcol)
  PRINT """
  <tr>
    <td>""" & dbcol{"cust_id"} & """</td>
    <td>""" & dbcol{"cust_name"} & """</td>
    <td>""" & dbcol{"cust_addr"} & """</td>
    <td>""" & dbcol{"cust_city"} & """</td>
    <td>""" & dbcol{"cust_state"} & """</td>
    <td>""" & dbcol{"cust_zip"} & """</td>
    <td>""" & dbcol{"cust_phone"} & """</td>
  </tr>
"""
WEND
PRINT """
  <tr>
    <td colspan=7 align="left"><br><input type="button" value="Add New Customer" onClick="parent.location='/mtsb/webcustomer.sb'"></td>
  </tr>
</table>
</body>
</html>
"""
END

Enter_Customer:

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()

PRINT """
<html>
<head>
<title>Customer Master File Maintenance</title>
</head>
<body>
<form name="custform" method="POST" action="/mtsb/webcustomer.sb">
<table border="0">
  <tr>
    <td align="right">ID</td>
    <td><input type="text" name="cust_id" size="5"></td>
  </tr>
  <tr>
    <td align="right">Name</td>
    <td><input type="text" name="cust_name" size="30"></td>
  </tr>
  <tr>
    <td align="right">Address</td>
    <td><input type="text" name="cust_addr" size="30"></td>
   </tr>
  <tr>
    <td align="right">City</td>
    <td><input type="text" name="cust_city" size="20"></td>
  </tr>
  <tr>
    <td align="right">State</td>
    <td><input type="text" name="cust_state" size="5"></td>
  </tr>
  <tr>
    <td align="right">Zip</td>
    <td><input type="text" name="cust_zip" size="5"></td>
  </tr>
  <tr>
    <td align="right">Phone</td>
    <td><input type="text" name="cust_phone" size="20"></td>
  </tr>
  <tr>
    <td colspan=2 align="center"><br><input type="submit" value="Add Customer"></td>
  </tr>
</table>
</form>
</body>
</html>
"""

MySQL - customer table
Code: [Select]
CREATE TABLE `sbtest`.`customer` (
`cust_id` VARCHAR( 5 ) NOT NULL ,
`cust_name` VARCHAR( 30 ) NULL ,
`cust_addr` VARCHAR( 30 ) NULL ,
`cust_city` VARCHAR( 20 ) NULL ,
`cust_state` VARCHAR( 5 ) NULL ,
`cust_zip` VARCHAR( 5 ) NULL ,
`cust_phone` VARCHAR( 20 ) NULL ,
PRIMARY KEY ( `cust_id` )
) ENGINE = MYISAM ;

JRS

  • Guest
Re: MTSB
« Reply #4 on: December 27, 2010, 06:36:16 PM »
Here is the old Address Book Code Challenge example I modified to use in this example. This version is using the MySQL API directly and not using the CGI client/server interface with the database. My next post will show the CGI version of the Gtk form.

gtkcustomer.sb
Code: [Select]
' All Basic address book challenge - 9/15/2008
'
' Basic Language: ScriptBasic 2.1 (Windows / Linux)
'
' Authors: Peter van Eerten - www.gtk-server.org
'          John Spikowski - www.scriptbasic.org

' GTK-server extension module
INCLUDE gtk.bas

' MySQL extension module
INCLUDE mysql.bas

' Connect to MySQL database
dbh = mysql::RealConnect("localhost","root","xxxx","sbtest")

SUB Fill_Data

' clear the list
gtk_list_store_clear(List_Store)

mysql::query(dbh,"SELECT * FROM customer")

WHILE mysql::FetchHash(dbh, dbrow)

gtk_list_store_append(List_Store, List_Iter)
gtk_list_store_set(List_Store, List_Iter, 0, dbrow{"cust_id"}, -1)
gtk_list_store_set(List_Store, List_Iter, 1, dbrow{"cust_name"}, -1)
gtk_list_store_set(List_Store, List_Iter, 2, dbrow{"cust_addr"}, -1)
gtk_list_store_set(List_Store, List_Iter, 3, dbrow{"cust_city"}, -1)
gtk_list_store_set(List_Store, List_Iter, 4, dbrow{"cust_state"}, -1)
gtk_list_store_set(List_Store, List_Iter, 5, dbrow{"cust_zip"}, -1)
gtk_list_store_set(List_Store, List_Iter, 6, dbrow{"cust_phone"}, -1)

WEND

END SUB


SUB Add_Edit_Data

' determine which mode we are
IF Add_Edit_Mode = 1 THEN
    gtk_list_store_append(List_Store, List_Iter)
END IF

' Get the etries, currently no checking
info[0] = gtk_entry_get_text(EntryID)
info[1] = gtk_entry_get_text(EntryName)
info[2] = gtk_entry_get_text(EntryAddress)
info[3] = gtk_entry_get_text(EntryCity)
info[4] = gtk_entry_get_text(EntryState)
info[5] = gtk_entry_get_text(EntryZip)
info[6] = gtk_entry_get_text(EntryPhone)

' Add mode, store the info into the list widget
FOR i = 0 TO 6
    gtk_list_store_set(List_Store, List_Iter, i, info[i], -1)
NEXT i

' Delete the entries
gtk_editable_delete_text(EntryID, 0, -1)
gtk_editable_delete_text(EntryName, 0, -1)
gtk_editable_delete_text(EntryAddress, 0, -1)
gtk_editable_delete_text(EntryCity, 0, -1)
gtk_editable_delete_text(EntryState, 0, -1)
gtk_editable_delete_text(EntryZip, 0, -1)
gtk_editable_delete_text(EntryPhone, 0, -1)

' Edit mode? Hide dialog
IF Add_Edit_Mode = 2 THEN
    gtk_widget_hide(Entry_Field)
    SQL = "UPDATE customer SET cust_id = '" & info[0] & "', cust_name = '" & info[1] & "', cust_addr = '" & info[2] & "', cust_city = '" & info[3] & "', cust_state = '" & info[4] & "', cust_zip = '" & info[5] & "', cust_phone = '" & info[6] & "'"
ELSE
    SQL = "INSERT INTO customer (cust_id, cust_name, cust_addr, cust_city, cust_state, cust_zip, cust_phone) VALUES ('" & info[0] & "', '" & info[1] & "', '" & info[2] & "', '" & info[3] & "', '" & info[4] & "', '" & info[5] & "', '" & info[6] & "')"
END IF

mysql::query(dbh, SQL)

END SUB


SUB Del_Data

' Check if a row is selected
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "1" THEN
    gtk_list_store_remove(List_Store, List_Iter)
    SQL = "DELETE FROM customer WHERE cust_id = " & info[0]
    mysql::query(dbh, SQL)
ELSE
    gtk_widget_show_all(Error_Msg)
END IF

END SUB

' MAIN program

' Optionally enable GTK logging
gtk_server_cfg("-log=log.txt")

' Get GLADE definition
xml = glade_xml_new("form.glade")
glade_xml_signal_autoconnect(xml)

' Get main window ID and connect signal
Main_Window = glade_xml_get_widget(xml, "Main_Window")
gtk_server_connect(Main_Window, "delete-event", "Main_Window")

' Get button IDs and connect signals
Add_Button = glade_xml_get_widget(xml, "Add_Button")
gtk_server_connect(Add_Button, "clicked", "Add_Button")
Del_Button = glade_xml_get_widget(xml, "Del_Button")
gtk_server_connect(Del_Button, "clicked", "Del_Button")
Edit_Button = glade_xml_get_widget(xml, "Edit_Button")
gtk_server_connect(Edit_Button, "clicked", "Edit_Button")
Exit_Button = glade_xml_get_widget(xml, "Exit_Button")
gtk_server_connect(Exit_Button, "clicked", "Exit_Button")

' Get scrolled window
Scrolled_Window =  glade_xml_get_widget(xml, "Scrolled_Window")

' Define the list
List_Iter = gtk_server_opaque()
GTK::gtk("gtk_server_redefine gtk_list_store_new NONE WIDGET 8 INT INT INT INT INT INT INT INT")
List_Store = GTK::gtk("gtk_list_store_new 7 64 64 64 64 64 64 64")
List_Choice = gtk_tree_view_new_with_model(List_Store)
GTK::gtk("gtk_server_connect " & List_Choice & " button-press-event " & List_Choice & " 1")
gtk_tree_view_set_headers_visible(List_Choice, 1)
gtk_tree_view_set_headers_clickable(List_Choice, 1)
gtk_tree_view_set_grid_lines(List_Choice, 3)
gtk_tree_sortable_set_sort_column_id(List_Store, 0, 0)
Tree_Sel = gtk_tree_view_get_selection(List_Choice)
gtk_tree_selection_set_mode(Tree_Sel, 2)
Txt_Cell = gtk_cell_renderer_text_new()
List_Column0 = gtk_tree_view_column_new_with_attributes("ID", Txt_Cell, "text", 0, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column0)
gtk_tree_view_column_set_resizable(List_Column0, 1)
gtk_tree_view_column_set_clickable(List_Column0, 1)
List_Column1 = gtk_tree_view_column_new_with_attributes("Name", Txt_Cell, "text", 1, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column1)
gtk_tree_view_column_set_resizable(List_Column1, 1)
List_Column2 = gtk_tree_view_column_new_with_attributes("Address", Txt_Cell, "text", 2, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column2)
gtk_tree_view_column_set_resizable(List_Column2, 1)
List_Column3 = gtk_tree_view_column_new_with_attributes("City", Txt_Cell, "text", 3, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column3)
gtk_tree_view_column_set_resizable(List_Column3, 1)
List_Column4 = gtk_tree_view_column_new_with_attributes("State", Txt_Cell, "text", 4, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column4)
gtk_tree_view_column_set_resizable(List_Column4, 1)
List_Column5 = gtk_tree_view_column_new_with_attributes("ZIP", Txt_Cell, "text", 5, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column5)
gtk_tree_view_column_set_resizable(List_Column5, 1)
List_Column6 = gtk_tree_view_column_new_with_attributes("Phone", Txt_Cell, "text", 6, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column6)
gtk_tree_view_column_set_resizable(List_Column6, 1)
gtk_container_add(Scrolled_Window, List_Choice)
gtk_widget_show_all(Scrolled_Window)

' Add entry window
Entry_Field = glade_xml_get_widget(xml, "Entry_Field")
gtk_server_connect(Entry_Field, "delete-event", "Entry_Field")

' Get button ID and connect signal
Entry_Ok_Button = glade_xml_get_widget(xml, "Entry_Ok_Button")
gtk_server_connect(Entry_Ok_Button, "clicked", "Entry_Ok_Button")
Entry_Can_Button = glade_xml_get_widget(xml, "Entry_Can_Button")
gtk_server_connect(Entry_Can_Button, "clicked", "Entry_Can_Button")

' Get ID's for the textentries
EntryID = glade_xml_get_widget(xml, "EntryID")
EntryName = glade_xml_get_widget(xml, "EntryName")
EntryAddress = glade_xml_get_widget(xml, "EntryAddress")
EntryCity = glade_xml_get_widget(xml, "EntryCity")
EntryState = glade_xml_get_widget(xml, "EntryState")
EntryZip = glade_xml_get_widget(xml, "EntryZip")
EntryPhone = glade_xml_get_widget(xml, "EntryPhone")

' Warning dialog, define here because of a bug in Glade
Warning_Msg = gtk_message_dialog_new(Main_Window, 1, 1, 4, "\nAre you sure to delete this entry?", "''")
gtk_window_set_title(Warning_Msg, "Warning")
gtk_server_connect(Warning_Msg, "delete-event", "Warning_Msg")
gtk_server_connect(Warning_Msg, "response", "Warning_Msg_Response")

' Warning dialog, define here because of a bug in Glade
Error_Msg = gtk_message_dialog_new(Main_Window, 1, 3, 2, "\nSelect an entry first!", "''")
gtk_window_set_title(Error_Msg, "Error!")
gtk_server_connect(Error_Msg, "delete-event", "Error_Msg")

' Fill grid with data
CALL Fill_Data()

' Set sort flag
Sort_Flag = 0

' Set edit or add mode - 1=ADD, 2=EDIT
Add_Edit_Mode = 1

' Mainloop starts here
REPEAT

    ' Get event
    event = gtk_server_callback("wait")

    ' Add entry form
    IF event = "Add_Button" THEN
Add_Edit_Mode = 1
gtk_window_set_title(Entry_Field, "Add entry")
gtk_widget_show_all(Entry_Field)
gtk_widget_grab_focus(EntryID)
    END IF

    ' Edit entry form
    IF event = "Edit_Button" THEN
Add_Edit_Mode = 2
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "0" THEN
    gtk_widget_show_all(Error_Msg)
ELSE
    FOR i = 0 TO 6
info[i] = gtk_tree_model_get(List_Store, List_Iter, i, "''", -1)
    NEXT i

    ' Get the etries, currently no checking
    gtk_entry_set_text(EntryID, MID(info[0], 4))
    gtk_entry_set_text(EntryName, MID(info[1], 4))
    gtk_entry_set_text(EntryAddress, MID(info[2], 4))
    gtk_entry_set_text(EntryCity, MID(info[3], 4))
    gtk_entry_set_text(EntryState, MID(info[4], 4))
    gtk_entry_set_text(EntryZip, MID(info[5], 4))
    gtk_entry_set_text(EntryPhone, MID(info[6], 4))

    gtk_window_set_title(Entry_Field, "Edit entry")
    gtk_widget_show_all(Entry_Field)
    gtk_widget_grab_focus(EntryID)

END IF
    END IF

    ' These are the buttons on the entry form
    IF event = "Entry_Can_Button" OR event = "Entry_Field" THEN
gtk_widget_hide(Entry_Field)

' Delete the entries
gtk_editable_delete_text(EntryID, 0, -1)
gtk_editable_delete_text(EntryName, 0, -1)
gtk_editable_delete_text(EntryAddress, 0, -1)
gtk_editable_delete_text(EntryCity, 0, -1)
gtk_editable_delete_text(EntryState, 0, -1)
gtk_editable_delete_text(EntryZip, 0, -1)
gtk_editable_delete_text(EntryPhone, 0, -1)
    END IF
    IF event = "Entry_Ok_Button" THEN
CALL Add_Edit_Data()
    END IF

    ' Warning dialog
    IF event = "Del_Button" THEN
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "0" THEN
    gtk_widget_show_all(Error_Msg)
ELSE
    gtk_widget_show_all(Warning_Msg)
END IF
    END IF
    IF event = "Warning_Msg" THEN gtk_widget_hide(Warning_Msg)
    IF event = "Warning_Msg_Response" THEN
response = gtk_server_callback_value(1, "INT")
IF response = -8 THEN CALL Del_Data()
gtk_widget_hide(Warning_Msg)
    END IF

    ' Error dialog
    IF event = "Error_Msg" OR event = Error_Msg THEN gtk_widget_hide(Error_Msg)

    ' Sortable on key
    IF event = List_Column0 THEN
Sort_Flag = 1 - Sort_Flag
gtk_tree_sortable_set_sort_column_id(List_Store, 0, Sort_Flag)
    END IF

UNTIL event = "Main_Window" OR event = "Exit_Button"

' Cleanup resources
gtk_server_exit()

form.glade
Code: [Select]
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.4 on Sun Aug 31 20:12:55 2008 -->
<glade-interface>
  <widget class="GtkWindow" id="Entry_Field">
    <property name="title" translatable="yes">Entry</property>
    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
    <property name="destroy_with_parent">True</property>
    <property name="icon">supertux.xpm</property>
    <property name="transient_for">Main_Window</property>
    <child>
      <widget class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <child>
          <placeholder/>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox1">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkLabel" id="LabelId">
                <property name="width_request">100</property>
                <property name="visible">True</property>
                <property name="xalign">1</property>
                <property name="label" translatable="yes">ID: </property>
              </widget>
              <packing>
                <property name="expand">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkEntry" id="EntryID">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">1</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox2">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkLabel" id="labelNAME">
                <property name="width_request">100</property>
                <property name="visible">True</property>
                <property name="xalign">1</property>
                <property name="label" translatable="yes">Name: </property>
              </widget>
              <packing>
                <property name="expand">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkEntry" id="EntryName">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">1</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox3">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkLabel" id="label1">
                <property name="width_request">100</property>
                <property name="visible">True</property>
                <property name="xalign">1</property>
                <property name="label" translatable="yes">Address: </property>
              </widget>
              <packing>
                <property name="expand">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkEntry" id="EntryAddress">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">1</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox4">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkLabel" id="label2">
                <property name="width_request">100</property>
                <property name="visible">True</property>
                <property name="xalign">1</property>
                <property name="label" translatable="yes">City: </property>
              </widget>
              <packing>
                <property name="expand">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkEntry" id="EntryCity">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">1</property>
            <property name="position">4</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox5">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkLabel" id="label3">
                <property name="width_request">100</property>
                <property name="visible">True</property>
                <property name="xalign">1</property>
                <property name="label" translatable="yes">State: </property>
              </widget>
              <packing>
                <property name="expand">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkEntry" id="EntryState">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">1</property>
            <property name="position">5</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox6">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkLabel" id="label4">
                <property name="width_request">100</property>
                <property name="visible">True</property>
                <property name="xalign">1</property>
                <property name="label" translatable="yes">ZIP code: </property>
              </widget>
              <packing>
                <property name="expand">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkEntry" id="EntryZip">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">1</property>
            <property name="position">6</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox7">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkLabel" id="label5">
                <property name="width_request">100</property>
                <property name="visible">True</property>
                <property name="xalign">1</property>
                <property name="label" translatable="yes">Phone: </property>
              </widget>
              <packing>
                <property name="expand">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkEntry" id="EntryPhone">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
              </widget>
              <packing>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">1</property>
            <property name="position">7</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHSeparator" id="hseparator1">
            <property name="visible">True</property>
          </widget>
          <packing>
            <property name="expand">False</property>
            <property name="padding">4</property>
            <property name="position">8</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox8">
            <property name="visible">True</property>
            <property name="border_width">3</property>
            <child>
              <widget class="GtkButton" id="Entry_Can_Button">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="label" translatable="yes">gtk-close</property>
                <property name="use_stock">True</property>
                <property name="response_id">0</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
            <child>
              <widget class="GtkButton" id="Entry_Ok_Button">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="label" translatable="yes">gtk-save</property>
                <property name="use_stock">True</property>
                <property name="response_id">0</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="pack_type">GTK_PACK_END</property>
                <property name="position">2</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="padding">1</property>
            <property name="pack_type">GTK_PACK_END</property>
            <property name="position">9</property>
          </packing>
        </child>
      </widget>
    </child>
  </widget>
  <widget class="GtkWindow" id="Main_Window">
    <property name="width_request">500</property>
    <property name="height_request">400</property>
    <property name="visible">True</property>
    <property name="title" translatable="yes">Customer File Maintence</property>
    <property name="icon">supertux.xpm</property>
    <child>
      <widget class="GtkVBox" id="vbox2">
        <property name="visible">True</property>
        <child>
          <widget class="GtkMenuBar" id="menubar1">
            <property name="visible">True</property>
            <child>
              <widget class="GtkMenuItem" id="menuitem5">
                <property name="visible">True</property>
                <property name="label" translatable="yes">_File</property>
                <property name="use_underline">True</property>
                <child>
                  <widget class="GtkMenu" id="menu4">
                    <property name="visible">True</property>
                    <child>
                      <widget class="GtkImageMenuItem" id="imagemenuitem15">
                        <property name="visible">True</property>
                        <property name="label" translatable="yes">gtk-quit</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </widget>
                    </child>
                  </widget>
                </child>
              </widget>
            </child>
            <child>
              <widget class="GtkMenuItem" id="menuitem8">
                <property name="visible">True</property>
                <property name="label" translatable="yes">_Help</property>
                <property name="use_underline">True</property>
                <child>
                  <widget class="GtkMenu" id="menu6">
                    <property name="visible">True</property>
                    <child>
                      <widget class="GtkImageMenuItem" id="imagemenuitem20">
                        <property name="visible">True</property>
                        <property name="label" translatable="yes">gtk-about</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </widget>
                    </child>
                  </widget>
                </child>
              </widget>
            </child>
          </widget>
          <packing>
            <property name="expand">False</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHBox" id="hbox9">
            <property name="visible">True</property>
            <property name="border_width">5</property>
            <child>
              <widget class="GtkFrame" id="frame1">
                <property name="visible">True</property>
                <property name="label_xalign">0</property>
                <child>
                  <widget class="GtkAlignment" id="alignment1">
                    <property name="visible">True</property>
                    <property name="top_padding">5</property>
                    <property name="bottom_padding">5</property>
                    <property name="left_padding">5</property>
                    <property name="right_padding">5</property>
                    <child>
                      <widget class="GtkScrolledWindow" id="Scrolled_Window">
                        <property name="visible">True</property>
                        <property name="can_focus">True</property>
                        <property name="extension_events">GDK_EXTENSION_EVENTS_CURSOR</property>
                        <property name="border_width">2</property>
                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
                        <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
                        <child>
                          <placeholder/>
                        </child>
                      </widget>
                    </child>
                  </widget>
                </child>
                <child>
                  <placeholder/>
                  <packing>
                    <property name="type">label_item</property>
                  </packing>
                </child>
              </widget>
              <packing>
                <property name="padding">5</property>
              </packing>
            </child>
            <child>
              <widget class="GtkFrame" id="frame2">
                <property name="visible">True</property>
                <property name="label_xalign">0</property>
                <child>
                  <widget class="GtkVBox" id="vbox3">
                    <property name="visible">True</property>
                    <property name="border_width">5</property>
                    <child>
                      <widget class="GtkButton" id="Add_Button">
                        <property name="visible">True</property>
                        <property name="can_focus">True</property>
                        <property name="receives_default">True</property>
                        <property name="label" translatable="yes">gtk-add</property>
                        <property name="use_stock">True</property>
                        <property name="response_id">0</property>
                      </widget>
                      <packing>
                        <property name="expand">False</property>
                        <property name="padding">2</property>
                      </packing>
                    </child>
                    <child>
                      <widget class="GtkButton" id="Del_Button">
                        <property name="visible">True</property>
                        <property name="can_focus">True</property>
                        <property name="receives_default">True</property>
                        <property name="label" translatable="yes">gtk-remove</property>
                        <property name="use_stock">True</property>
                        <property name="response_id">0</property>
                      </widget>
                      <packing>
                        <property name="expand">False</property>
                        <property name="padding">2</property>
                        <property name="position">1</property>
                      </packing>
                    </child>
                    <child>
                      <widget class="GtkButton" id="Edit_Button">
                        <property name="visible">True</property>
                        <property name="can_focus">True</property>
                        <property name="receives_default">True</property>
                        <property name="label" translatable="yes">gtk-edit</property>
                        <property name="use_stock">True</property>
                        <property name="response_id">0</property>
                      </widget>
                      <packing>
                        <property name="expand">False</property>
                        <property name="padding">2</property>
                        <property name="position">2</property>
                      </packing>
                    </child>
                    <child>
                      <placeholder/>
                    </child>
                    <child>
                      <widget class="GtkButton" id="Exit_Button">
                        <property name="visible">True</property>
                        <property name="can_focus">True</property>
                        <property name="receives_default">True</property>
                        <property name="label" translatable="yes">gtk-quit</property>
                        <property name="use_stock">True</property>
                        <property name="response_id">0</property>
                      </widget>
                      <packing>
                        <property name="expand">False</property>
                        <property name="padding">2</property>
                        <property name="pack_type">GTK_PACK_END</property>
                        <property name="position">4</property>
                      </packing>
                    </child>
                  </widget>
                </child>
                <child>
                  <placeholder/>
                  <packing>
                    <property name="type">label_item</property>
                  </packing>
                </child>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="padding">5</property>
                <property name="position">1</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <widget class="GtkStatusbar" id="statusbar1">
            <property name="visible">True</property>
            <property name="spacing">2</property>
          </widget>
          <packing>
            <property name="expand">False</property>
            <property name="position">2</property>
          </packing>
        </child>
      </widget>
    </child>
  </widget>
</glade-interface>

JRS

  • Guest
Re: MTSB
« Reply #5 on: December 27, 2010, 10:59:20 PM »
Here is the CGI client/server version of the GTK form. There are a couple of bugs in the original version I need to fix but the data interface works as it should.

csgtkcustomer.sb
Code: [Select]
' All Basic address book challenge - 9/15/2008
'
' Basic Language: ScriptBasic 2.1 (Windows / Linux)
'
' Authors: Peter van Eerten - www.gtk-server.org
'          John Spikowski - www.scriptbasic.org

' GTK-server extension module
INCLUDE gtk.bas

' cURL extension module
INCLUDE curl.bas

GLOBAL CONST TAB = "\t"
GLOBAL CONST NL = "\n"

ch = curl::init()

SUB Fill_Data

' clear the list
gtk_list_store_clear(List_Store)

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/gtkdbweb.sb")
curl::option(ch,"COOKIEJAR","/home/jrs/SB/test/cookie.txt")
db_response = curl::perform(ch)

WHILE LEN(db_response)
  eor = INSTR(db_response,NL)
  this_row = LEFT(db_response, eor - 1)
  SPLIT this_row BY TAB TO dbrow{"cust_id"},dbrow{"cust_name"},dbrow{"cust_addr"},dbrow{"cust_city"},dbrow{"cust_state"},dbrow{"cust_zip"},dbrow{"cust_phone"}
  gtk_list_store_append(List_Store, List_Iter)
  gtk_list_store_set(List_Store, List_Iter, 0, dbrow{"cust_id"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 1, dbrow{"cust_name"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 2, dbrow{"cust_addr"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 3, dbrow{"cust_city"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 4, dbrow{"cust_state"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 5, dbrow{"cust_zip"}, -1)
  gtk_list_store_set(List_Store, List_Iter, 6, dbrow{"cust_phone"}, -1)
  db_response = MID(db_response, eor + 1)
WEND

END SUB

SUB Add_Edit_Data

' determine which mode we are
IF Add_Edit_Mode = 1 THEN
    gtk_list_store_append(List_Store, List_Iter)
END IF

' Get the etries, currently no checking
info[0] = gtk_entry_get_text(EntryID)
info[1] = gtk_entry_get_text(EntryName)
info[2] = gtk_entry_get_text(EntryAddress)
info[3] = gtk_entry_get_text(EntryCity)
info[4] = gtk_entry_get_text(EntryState)
info[5] = gtk_entry_get_text(EntryZip)
info[6] = gtk_entry_get_text(EntryPhone)

' Add mode, store the info into the list widget
FOR i = 0 TO 6
    gtk_list_store_set(List_Store, List_Iter, i, info[i], -1)
NEXT i

' Delete the entries
gtk_editable_delete_text(EntryID, 0, -1)
gtk_editable_delete_text(EntryName, 0, -1)
gtk_editable_delete_text(EntryAddress, 0, -1)
gtk_editable_delete_text(EntryCity, 0, -1)
gtk_editable_delete_text(EntryState, 0, -1)
gtk_editable_delete_text(EntryZip, 0, -1)
gtk_editable_delete_text(EntryPhone, 0, -1)

post_str = "cust_id=" & curl::escape(info[0]) & "&cust_name=" & curl::escape(info[1]) & "&cust_addr=" & curl::escape(info[2]) & "&cust_city=" & curl::escape(info[3]) & "&cust_state=" & curl::escape(info[4]) & "&cust_zip=" & curl::escape(info[5]) & "&cust_phone=" & curl::escape(info[6]) & "&maint_flag=" & curl::escape(Add_Edit_Mode)

curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/gtkdbweb.sb")
curl::option(ch,"COOKIE","webcust")
curl::option(ch,"POST")
curl::option(ch,"POSTFIELDS",post_str)
txt = curl::perform(ch)

END SUB

SUB Del_Data

' Check if a row is selected
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "1" THEN
   gtk_list_store_remove(List_Store, List_Iter)
   post_str = "cust_id=" & curl::escape(info[0]) & "&maint_flag=" & "3"
   curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/gtkdbweb.sb")
   curl::option(ch,"COOKIE","webcust")
   curl::option(ch,"POST")
   curl::option(ch,"POSTFIELDS",post_str)
   txt = curl::perform(ch)
ELSE
    gtk_widget_show_all(Error_Msg)
END IF

END SUB

' MAIN program

' Optionally enable GTK logging
gtk_server_cfg("-log=log.txt")

' Get GLADE definition
xml = glade_xml_new("form.glade")
glade_xml_signal_autoconnect(xml)

' Get main window ID and connect signal
Main_Window = glade_xml_get_widget(xml, "Main_Window")
gtk_server_connect(Main_Window, "delete-event", "Main_Window")

' Get button IDs and connect signals
Add_Button = glade_xml_get_widget(xml, "Add_Button")
gtk_server_connect(Add_Button, "clicked", "Add_Button")
Del_Button = glade_xml_get_widget(xml, "Del_Button")
gtk_server_connect(Del_Button, "clicked", "Del_Button")
Edit_Button = glade_xml_get_widget(xml, "Edit_Button")
gtk_server_connect(Edit_Button, "clicked", "Edit_Button")
Exit_Button = glade_xml_get_widget(xml, "Exit_Button")
gtk_server_connect(Exit_Button, "clicked", "Exit_Button")

' Get scrolled window
Scrolled_Window =  glade_xml_get_widget(xml, "Scrolled_Window")

' Define the list
List_Iter = gtk_server_opaque()
GTK::gtk("gtk_server_redefine gtk_list_store_new NONE WIDGET 8 INT INT INT INT INT INT INT INT")
List_Store = GTK::gtk("gtk_list_store_new 7 64 64 64 64 64 64 64")
List_Choice = gtk_tree_view_new_with_model(List_Store)
GTK::gtk("gtk_server_connect " & List_Choice & " button-press-event " & List_Choice & " 1")
gtk_tree_view_set_headers_visible(List_Choice, 1)
gtk_tree_view_set_headers_clickable(List_Choice, 1)
gtk_tree_view_set_grid_lines(List_Choice, 3)
gtk_tree_sortable_set_sort_column_id(List_Store, 0, 0)
Tree_Sel = gtk_tree_view_get_selection(List_Choice)
gtk_tree_selection_set_mode(Tree_Sel, 2)
Txt_Cell = gtk_cell_renderer_text_new()
List_Column0 = gtk_tree_view_column_new_with_attributes("ID", Txt_Cell, "text", 0, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column0)
gtk_tree_view_column_set_resizable(List_Column0, 1)
gtk_tree_view_column_set_clickable(List_Column0, 1)
List_Column1 = gtk_tree_view_column_new_with_attributes("Name", Txt_Cell, "text", 1, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column1)
gtk_tree_view_column_set_resizable(List_Column1, 1)
List_Column2 = gtk_tree_view_column_new_with_attributes("Address", Txt_Cell, "text", 2, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column2)
gtk_tree_view_column_set_resizable(List_Column2, 1)
List_Column3 = gtk_tree_view_column_new_with_attributes("City", Txt_Cell, "text", 3, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column3)
gtk_tree_view_column_set_resizable(List_Column3, 1)
List_Column4 = gtk_tree_view_column_new_with_attributes("State", Txt_Cell, "text", 4, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column4)
gtk_tree_view_column_set_resizable(List_Column4, 1)
List_Column5 = gtk_tree_view_column_new_with_attributes("ZIP", Txt_Cell, "text", 5, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column5)
gtk_tree_view_column_set_resizable(List_Column5, 1)
List_Column6 = gtk_tree_view_column_new_with_attributes("Phone", Txt_Cell, "text", 6, "NULL")
gtk_tree_view_append_column(List_Choice, List_Column6)
gtk_tree_view_column_set_resizable(List_Column6, 1)
gtk_container_add(Scrolled_Window, List_Choice)
gtk_widget_show_all(Scrolled_Window)

' Add entry window
Entry_Field = glade_xml_get_widget(xml, "Entry_Field")
gtk_server_connect(Entry_Field, "delete-event", "Entry_Field")

' Get button ID and connect signal
Entry_Ok_Button = glade_xml_get_widget(xml, "Entry_Ok_Button")
gtk_server_connect(Entry_Ok_Button, "clicked", "Entry_Ok_Button")
Entry_Can_Button = glade_xml_get_widget(xml, "Entry_Can_Button")
gtk_server_connect(Entry_Can_Button, "clicked", "Entry_Can_Button")

' Get ID's for the textentries
EntryID = glade_xml_get_widget(xml, "EntryID")
EntryName = glade_xml_get_widget(xml, "EntryName")
EntryAddress = glade_xml_get_widget(xml, "EntryAddress")
EntryCity = glade_xml_get_widget(xml, "EntryCity")
EntryState = glade_xml_get_widget(xml, "EntryState")
EntryZip = glade_xml_get_widget(xml, "EntryZip")
EntryPhone = glade_xml_get_widget(xml, "EntryPhone")

' Warning dialog, define here because of a bug in Glade
Warning_Msg = gtk_message_dialog_new(Main_Window, 1, 1, 4, "\nAre you sure to delete this entry?", "''")
gtk_window_set_title(Warning_Msg, "Warning")
gtk_server_connect(Warning_Msg, "delete-event", "Warning_Msg")
gtk_server_connect(Warning_Msg, "response", "Warning_Msg_Response")

' Warning dialog, define here because of a bug in Glade
Error_Msg = gtk_message_dialog_new(Main_Window, 1, 3, 2, "\nSelect an entry first!", "''")
gtk_window_set_title(Error_Msg, "Error!")
gtk_server_connect(Error_Msg, "delete-event", "Error_Msg")

' Fill grid with data
CALL Fill_Data()

' Set sort flag
Sort_Flag = 0

' Set edit or add mode - 1=ADD, 2=EDIT
Add_Edit_Mode = 1

' Mainloop starts here
REPEAT

    ' Get event
    event = gtk_server_callback("wait")

    ' Add entry form
    IF event = "Add_Button" THEN
Add_Edit_Mode = 1
gtk_window_set_title(Entry_Field, "Add entry")
gtk_widget_show_all(Entry_Field)
gtk_widget_grab_focus(EntryID)
    END IF

    ' Edit entry form
    IF event = "Edit_Button" THEN
Add_Edit_Mode = 2
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "0" THEN
   gtk_widget_show_all(Error_Msg)
ELSE
   FOR i = 0 TO 6
info[i] = gtk_tree_model_get(List_Store, List_Iter, i, "''", -1)
   NEXT i

   ' Get the etries, currently no checking
   gtk_entry_set_text(EntryID, MID(info[0], 4))
   gtk_entry_set_text(EntryName, MID(info[1], 4))
   gtk_entry_set_text(EntryAddress, MID(info[2], 4))
   gtk_entry_set_text(EntryCity, MID(info[3], 4))
   gtk_entry_set_text(EntryState, MID(info[4], 4))
   gtk_entry_set_text(EntryZip, MID(info[5], 4))
   gtk_entry_set_text(EntryPhone, MID(info[6], 4))

   gtk_window_set_title(Entry_Field, "Edit entry")
   gtk_widget_show_all(Entry_Field)
   gtk_widget_grab_focus(EntryID)

END IF
    END IF

    ' These are the buttons on the entry form
    IF event = "Entry_Can_Button" OR event = "Entry_Field" THEN
gtk_widget_hide(Entry_Field)

' Delete the entries
gtk_editable_delete_text(EntryID, 0, -1)
gtk_editable_delete_text(EntryName, 0, -1)
gtk_editable_delete_text(EntryAddress, 0, -1)
gtk_editable_delete_text(EntryCity, 0, -1)
gtk_editable_delete_text(EntryState, 0, -1)
gtk_editable_delete_text(EntryZip, 0, -1)
gtk_editable_delete_text(EntryPhone, 0, -1)
    END IF
    IF event = "Entry_Ok_Button" THEN
CALL Add_Edit_Data()
    END IF

    ' Warning dialog
    IF event = "Del_Button" THEN
IF gtk_tree_selection_get_selected(Tree_Sel, "NULL", List_Iter) = "0" THEN
   gtk_widget_show_all(Error_Msg)
ELSE
   gtk_widget_show_all(Warning_Msg)
END IF
    END IF
    IF event = "Warning_Msg" THEN gtk_widget_hide(Warning_Msg)
    IF event = "Warning_Msg_Response" THEN
response = gtk_server_callback_value(1, "INT")
IF response = -8 THEN CALL Del_Data()
gtk_widget_hide(Warning_Msg)
    END IF

    ' Error dialog
    IF event = "Error_Msg" OR event = Error_Msg THEN gtk_widget_hide(Error_Msg)

    ' Sortable on key
    IF event = List_Column0 THEN
Sort_Flag = 1 - Sort_Flag
gtk_tree_sortable_set_sort_column_id(List_Store, 0, Sort_Flag)
    END IF

UNTIL event = "Main_Window" OR event = "Exit_Button"

' Cleanup resources
gtk_server_exit()
curl::finish(ch)

gtkdbweb.sb
Code: [Select]
' Program: gtkdbweb.sb
' Version: 1.0
'    Date: 2010-12-27
'      By: JRS

INCLUDE cgi.bas
INCLUDE mt.bas
INCLUDE mysql.bas

CONST TAB = "\t"
CONST NL = "\n"

dbh = mysql::RealConnect("localhost","root","xxxx","sbtest")

OPTION cgi$Method cgi::GET or cgi::POST

session_id = cgi::Cookie("webcust")

New_Session:

IF session_id = undef THEN
  session_id = mt::NewSessionId()
  mt::SetSessionId(session_id)
ELSE
  IF mt::CheckSessionId(session_id) = False THEN
    session_id = undef
    GOTO New_Session
  END IF
  mt::SetSessionId(session_id)
END IF

IF cgi::RequestMethod() = "POST" THEN GOTO Maintenance

mysql::query(dbh,"SELECT * FROM customer")

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()

WHILE mysql::FetchHash(dbh, dbrow)
  PRINT dbrow{"cust_id"} & TAB & dbrow{"cust_name"} & TAB & dbrow{"cust_addr"} & TAB & dbrow{"cust_city"} & TAB & dbrow{"cust_state"} & TAB & dbrow{"cust_zip"} & TAB & dbrow{"cust_phone"} & NL
WEND

mysql::Close(dbh)
END

Maintenance:

cust_id = cgi::PostParam("cust_id")
cust_name = cgi::PostParam("cust_name")
cust_addr = cgi::PostParam("cust_addr")
cust_city = cgi::PostParam("cust_city")
cust_state = cgi::PostParam("cust_state")
cust_zip = cgi::PostParam("cust_zip")
cust_phone = cgi::PostParam("cust_phone")
maint_flag = cgi::PostParam("maint_flag")

IF maint_flag = "1" THEN SQL = "INSERT INTO customer (cust_id, cust_name, cust_addr, cust_city, cust_state, cust_zip, cust_phone) VALUES ('" & cust_id & "', '" & cust_name & "', '" & cust_addr & "', '" & cust_city & "', '" & cust_state & "', '" & cust_zip & "', '" & cust_phone & "')"
IF maint_flag = "2" THEN SQL = "UPDATE customer SET cust_id = '" & cust_id & "', cust_name = '" & cust_name & "', cust_addr = '" & cust_addr & "', cust_city = '" & cust_city & "', cust_state = '" & cust_state & "', cust_zip = '" & cust_zip & "', cust_phone = '" & cust_phone & "'"
IF maint_flag = "3" THEN SQL = "DELETE FROM customer WHERE cust_id = " & cust_id
    
mysql::query(dbh, SQL)

cgi::Header(200, "text/html")
cgi::SetCookie("webcust", session_id)
cgi::FinishHeader()

mysql::Close(dbh)
END
« Last Edit: December 28, 2010, 12:25:46 AM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #6 on: December 29, 2010, 05:29:34 PM »
I feel like I'm on an island and no one can hear me. I can see this is going to be another test of my faith. Some would pull out the beads and start saying prayers, I count occurrences of words in the Bible.  ;)

I have been trying to solve an issue of the server releasing control back to the client and continue running on the server in it's own thread. Peter Verhas suggested I try closing channel zero on the server in my script but SB complained saying 1-512 are the only valid channels numbers. Not willing to give up easy, something I read in the SB docs on CGI methods gave me a clue that I might be able to do a HEAD request and return back to the client leaving the script to continue running till it sees an END.

Success!

testfaith.sb
Code: [Select]
INCLUDE curl.bas

ch = curl::init()

'Start 1st thread
curl::option(ch,"NOBODY")
curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/faith.cgi")
txt = curl::perform(ch)

'Start 2nd thread
curl::option(ch,"NOBODY")
curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/faith2.cgi")
txt = curl::perform(ch)

curl::finish(ch)


faith.cgi
Code: [Select]
INCLUDE cgi.bas

option cgi$Method cgi::Get or cgi::Upload or cgi::Head

cgi::Header(200, "text/html")
cgi::FinishHeader()

fname = "/home/jrs/SB/test/Bible.txt"

start_time = NOW
fsize = FILELEN(fname)
OPEN fname FOR INPUT AS #1
text = INPUT(fsize, #1)
CLOSE #1

strip = "()[]{}|<>/@0123456789*.,;:!#?%$&+=_~\"\\" & CHR(9) & CHR(10) & CHR(13)
FOR i = 1 TO LEN(strip)
  text = REPLACE(text, MID(strip, i, 1), " ")
NEXT i

SPLITA text BY " " TO word_list

OPEN "/home/jrs/SB/test/wc.raw" FOR OUTPUT AS #1
FOR x = 0 TO UBOUND(word_list)
  text_out = TRIM(word_list[x])
  IF LEN(text_out) THEN PRINT #1,LCASE(text_out),"\n"
NEXT x

CLOSE #1

ok = EXECUTE("sort /home/jrs/SB/test/wc.raw -o /home/jrs/SB/test/wc.srt", -1, PID)
OPEN "/home/jrs/SB/test/wc.srt" FOR INPUT AS #2
OPEN "/home/jrs/SB/test/wc.lst" FOR OUTPUT AS #3


last_word = ""
word_count = 0
word_total = 0

Next_Word:

IF EOF(2) THEN GOTO Done
LINE INPUT #2, this_word
this_word = CHOMP(this_word)
word_total += 1
IF last_word = "" THEN last_word = this_word
IF this_word = last_word THEN
  word_count += 1
  GOTO Next_Word
END IF

PRINT #3, last_word & " (" & word_count & ")\n"
last_word = this_word
word_count = 1
GOTO Next_Word  

Done:
PRINTNL #3
PRINT #3, word_total - 1, " words in ", NOW - start_time, " seconds.\n"
CLOSE #2
CLOSE #3

END

Note: faith2.cgi is the same as faith.cgi except I added a 2 to the end of each wc*.xxx work file name. I could have passed a thread count ID but this was only a two thread test so I was lazy.

Results:

Bible - Bible in text format. (4.2 MB)

Word List - All words in the Bible, sorted and the number of times they are used. (what this program does - should work with any text file)

wc.sb - original word count SB program I started from the command line with scriba.  
804895 words in 56 seconds.

faith.cgi - running as a single thread task
804895 words in 56 seconds.

faith.cgi - running along side faith2.cgi
804895 words in 121 seconds.

faith2.cgi - running along side faith.cgi
804895 words in 122 seconds.

As this was going on my testfaith.sb program returned back immediately after starting the program. (could have continued on doing something else or initiating new scripts)

I was up to my old work around tricks again and found an alternative solution to my original request asked of Peter.

Quote from: Peter Verhas
That is a solution that fits you. However this is not a solution you were asking for:

The output stream is not closed in your solution simply ignored which is just as good for you.


« Last Edit: December 30, 2010, 01:55:56 AM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #7 on: December 30, 2010, 11:24:07 PM »
I forgot to show how to set global variables when using MTSB.

Code: Text
  1. INCLUDE curl.bas
  2. ch = curl::init()
  3.  
  4. ' Set MT Variable
  5. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtsetvar.bas")
  6. curl::perform(ch)
  7.  
  8. ' Get MT Variable
  9. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtgetvar.bas")
  10. txt = curl::perform(ch)
  11. PRINT txt
  12.  
  13. curl::finish(ch)
  14.  

Code: Text
  1. PRINT "Status: 200\nContent-Type: text/html\n\n"
  2.  
  3. DECLARE SUB MTSET ALIAS "setmtvariable" LIB "mt"
  4.  
  5. MTSET("mymtvar","ScriptBasic")
  6.  

Code: Text
  1. PRINT "Status: 200\nContent-Type: text/html\n\n"
  2.  
  3. DECLARE SUB MTGET ALIAS "getmtvariable" LIB "mt"
  4.  
  5. PRINT MTGET("mymtvar"),"\n"
  6.  

jrs@Laptop:~/SB/test$ scriba mtvar.sb
ScriptBasic
jrs@Laptop:~/SB/test$

Here is an example of the MTSB script using cURL to run the mtgetvar.bas script which returns the results to mtsetvar.bas and then to mtvar.sb.

Code: Text
  1. INCLUDE curl.bas
  2. ch = curl::init()
  3.  
  4. ' Set MT Variable
  5. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtsetvar.bas")
  6. txt = curl::perform(ch)
  7. PRINT txt
  8.  
  9. curl::finish(ch)
  10.  

Code: Text
  1. PRINT "Status: 200\nContent-Type: text/html\n\n"
  2.  
  3. DECLARE SUB MTSET ALIAS "setmtvariable" LIB "mt"
  4.  
  5. MTSET("mymtvar","ScriptBasic")
  6.  
  7. INCLUDE curl.bas
  8. ch = curl::init()
  9.  
  10. ' Get MT Variable
  11. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtgetvar.bas")
  12. txt = curl::perform(ch)
  13. PRINT txt
  14.  
  15. curl::finish(ch)
  16.  

Code: Text
  1. PRINT "Status: 200\nContent-Type: text/html\n\n"
  2. DECLARE SUB MTGET ALIAS "getmtvariable" LIB "mt"
  3.  
  4. PRINT MTGET("mymtvar"),"\n"
  5.  

jrs@Laptop:~/SB/test$ scriba mtvar.sb
ScriptBasic
jrs@Laptop:~/SB/test$

Bonus Tip:

If working with associative arrays and you need a sequential dump of their assigned names and values, ScriptBasic supports mixed array definitions which is another topic in itself.

Code: [Select]
a{"one"}=1
a{"two"}=2
a{"three"}=3

FOR x=0 to UBOUND(a)
PRINT "[",x,"] = ",a[x],"\n"
NEXT

Code: [Select]
jrs@Laptop:~/SB/test$ scriba aatest.sb
[0] = one
[1] = 1
[2] = two
[3] = 2
[4] = three
[5] = 3
jrs@Laptop:~/SB/test$
« Last Edit: December 31, 2010, 02:14:02 AM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #8 on: December 31, 2010, 03:41:04 AM »
If I could get GTK-Server not to crash sbhttpd on exit, (only extension module that this happens with) I might be able to show embedding ScriptBasic in a thread.  ;D

JRS

  • Guest
Re: MTSB
« Reply #9 on: December 31, 2010, 10:54:58 PM »
News Flash!

The function gtk_server_exit will not only release it's own resources but will do the END in your program for you as well.  :o

Code: [Select]
DECLARE SUB DLL ALIAS "_idll" LIB "iDLL-Server"
DECLARE SUB DEFINE ALIAS "_idll_define" LIB "iDLL-Server"

DEFINE "gtk_init NONE NONE 2 NULL NULL"
DEFINE "gtk_window_new delete-event WIDGET 1 INT"
DEFINE "gtk_window_set_title NONE NONE 2 WIDGET STRING"
DEFINE "gtk_table_new NONE WIDGET 3 INT INT BOOL"
DEFINE "gtk_container_add NONE NONE 2 WIDGET WIDGET"
DEFINE "gtk_button_new_with_label clicked WIDGET 1 STRING"
DEFINE "gtk_table_attach_defaults NONE NONE 6 WIDGET WIDGET INT INT INT INT"
DEFINE "gtk_widget_show_all NONE NONE 1 WIDGET"
DEFINE "gtk_server_callback NONE STRING 1 STRING"
DEFINE "gtk_server_exit NONE NONE 0"
DEFINE "gtk_widget_hide NONE NONE 1 WIDGET"
DEFINE "gtk_exit NONE NONE 1 INT"
DEFINE "gtk_widget_destroy NONE NONE 1 WIDGET"

DLL("gtk_init NULL NULL")
win = DLL("gtk_window_new 0")
DLL("gtk_window_set_title " & win & " \"SB Gtk\"")
tbl = DLL("gtk_table_new 10 10 1")
DLL("gtk_container_add " & win & " " & tbl)
but = DLL("gtk_button_new_with_label \"Quit\"")
DLL("gtk_table_attach_defaults " & tbl & " " & but & " 5 9 5 9")
DLL("gtk_widget_show_all " & win)

REPEAT
  event = DLL("gtk_server_callback WAIT")
UNTIL event = win OR event = but

DLL("gtk_server_exit")

PRINT "Press Any Key: "
LINE INPUT anykey

END

I never see Press Any Key:. This is running in standard scriba, not sbhttpd.

JRS

  • Guest
Re: MTSB
« Reply #10 on: January 01, 2011, 02:35:29 AM »
I decided to take a break from debugging the GTK-Server exit issue and try embedding ScriptBasic in a thread. GTK-Server works fine under sbhttpd/scriba until it tries to exit.

Code: Text
  1. INCLUDE curl.bas
  2.  
  3. ch = curl::init()
  4.  
  5. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/threadembed.sb")
  6. txt = curl::perform(ch)
  7. PRINT txt
  8.  
  9. curl::finish(ch)
  10.  

Code: Text
  1. PRINT "Status: 200\nContent-Type: text/html\n\n"
  2.  
  3. DECLARE SUB DLL ALIAS "_idll" LIB "iDLL-Server"
  4. DECLARE SUB DEFINE ALIAS "_idll_define" LIB "iDLL-Server"
  5. DECLARE SUB REQUIRE ALIAS "_idll_require" LIB "iDLL-Server"
  6.  
  7. REQUIRE "libiDLL.so"
  8. DEFINE "sb_new NONE POINTER 0"
  9. DEFINE "scriba_LoadConfiguration NONE INT 2 POINTER STRING"
  10. DEFINE "scriba_SetFileName NONE INT 2 POINTER STRING"
  11. DEFINE "scriba_Run NONE INT 2 POINTER STRING"
  12. DEFINE "scriba_LoadSourceProgram NONE INT 1 POINTER"
  13. DEFINE "scriba_destroy NONE NONE 1 POINTER"
  14.  
  15. pProgram = DLL("sb_new")
  16. DLL("scriba_LoadConfiguration " & pProgram & " \"/etc/scriba/basic.conf\"")
  17. DLL("scriba_SetFileName " & pProgram & " \"/home/jrs/SB/mtsb/E01.bas\"")
  18. DLL("scriba_LoadSourceProgram " & pProgram)
  19. DLL("scriba_Run " & pProgram & " \"JRS\"")
  20. DLL("scriba_destroy " & pProgram)
  21.  

Code: Text
  1. cmd = COMMAND()
  2.  
  3. PRINT "ARG = ",cmd,"\n"
  4.  
  5. FOR x = 1 TO 10
  6.   PRINT x,"\n"
  7. NEXT
  8.  

root@Laptop:~# ARG = JRS
1
2
3
4
5
6
7
8
9
10

root@Laptop:~#

The only strange thing is that the output of the embedded scriba went to the root console I started sbhttpd in and didn't get returned in the cURL response as I would have expected. It's still cool to see ScriptBasic create an instance of itself in a thread of itself. I think this proves the embedding ScriptBasic in your application is painless and non-intrusive.

Update

Actually the program did exactly what it was suppose to do. When you embed ScriptBasic (in sbhttpd in this case) it uses the stdin/out of the host as a default unless you redefine it with a SB API call.

« Last Edit: January 01, 2011, 02:49:43 AM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #11 on: January 01, 2011, 01:09:06 PM »
I think I know what the problem is with GTK-Server crashing ScriptBasic with it's gtk_server_exit() function.

Peter used the minimal SB framework to encapsulate GTK-Server for an extension module.

Code: [Select]
#ifdef GTK_SERVER_SCRIPTBASIC
besVERSION_NEGOTIATE
    return (int)INTERFACE_VERSION;
besEND

besSUB_START

besEND

besSUB_FINISH

besEND

besFUNCTION(_gtk)
    VARIABLE ptr;
    char* arg0;
    char* retstr;

    if(besARGNR>1) return EX_ERROR_TOO_MANY_ARGUMENTS;
    if(besARGNR<1) return EX_ERROR_TOO_FEW_ARGUMENTS;

    ptr = besARGUMENT(1);
    besDEREFERENCE(ptr);
    arg0 = besCONVERT2ZCHAR(besCONVERT2STRING(ptr),arg0);

    retstr = (char*)gtk(arg0);

    besALLOC_RETURN_STRING(strlen(retstr));
    memcpy(STRINGVALUE(besRETURNVALUE),retstr,strlen(retstr));

    besFREE(arg0);
besEND

/* Needed to compile standalone Scriptbasic programs */
SLFST VISIO_SLFST[] = {

    {"versmodu", versmodu},
    {"bootmodu", bootmodu},
    {"finimodu", finimodu},
    {"_gtk", _gtk},
    {NULL, NULL}
};

#endif /* End of ScriptBasic code */

Code: [Select]
   /* Check if the GTK-server must be exited */
    else if (!strcmp("gtk_server_exit", gtk_api_call)) {
if (gtkserver.mode == 1){
   printf("ok\n");
   fflush(stdout);
}
#ifdef GTK_SERVER_UNIX
/* Send signal to parent process */
if (gtkserver.behave & 8) kill(gtkserver.ppid, gtkserver.exit_sig);
#endif
/* OK string */
retstr = Print_Result("%s%sok%s", 3, gtkserver.pre, gtkserver.handle, gtkserver.post);
#ifdef GTK_SERVER_GTK1x
gtk_exit(0);
#elif GTK_SERVER_GTK2x
gtk_exit(0);
#elif GTK_SERVER_XF
fl_finish();
exit(EXIT_SUCCESS);
#else
exit(EXIT_SUCCESS);
#endif

Quote
Syntax:

#include <stdlib.h>
void exit( int exit_code );

Description:
The exit() function stops the program. exit_code is passed on to be the return value of the program, where usually zero indicates success and non-zero indicates an error.

Another thing I noticed is that GTK-Server isn't setting a gtkserver.behave code if GTK_SERVER_SCRIPTBASIC is defined.

Peter says he is doing a generic C exit() call. That's great if your running GTK-Server as a stand alone executable. If you're going to create a ScriptBasic extension module, you have to follow the specs defined in the SB developers guide and use the framework functions provide. My guess at this point is that GTK-Server is doing an out of sequence exit and trashing scriba/sbhttpd in the process. I think this is a simple fix if I could find an alternative to a hard exit() function and make it part of the besSUB_FINISH routine.

« Last Edit: January 02, 2011, 09:30:41 PM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #12 on: January 01, 2011, 06:00:40 PM »
I was able to get it to work. (sort of)

Code: [Select]
DLL("gtk_widget_hide " & win)
DLL("gtk_server_callback 2")

This doesn't shut down sbhttpd but I can't start another GTK-Server based window in another thread either. (Gtk X-Windows Errors)

Both gtk_server_exit and gtk_exit both do a EXIT() and shuts down sbhttpd. I guess GTK-Server was never intended to run in a threaded environment.  >:(

Quote
The program '<unknown>' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadIDChoice (invalid resource ID chosen for this connection)'.
  (Details: serial 595 error_code 14 request_code 1 minor_code 0)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the --sync command line
   option to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)

Looks like this is about it with pushing the limits of GTK-Server. I shouldn't bitch too much as I did get it to run in a thread even though it doesn't tolerate company. I was really hoping that I could get GTK-Server working in multiple threads with a shared memory option. I came pretty close to achieving those goals.
« Last Edit: January 01, 2011, 06:43:55 PM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #13 on: January 01, 2011, 08:04:36 PM »
Don't leave your seats just yet. There may be a possibility Gtk can run in a multi-threaded environment.

Read this ...

gdk-Threads

Code: [Select]
int
main (int argc, char *argv[])
{
  GtkWidget *window;
  g_thread_init (NULL);
  gdk_threads_init ();
  gdk_threads_enter ();
  gtk_init (&argc, &argv);
  window = create_window ();
  gtk_widget_show (window);
  gtk_main ();
  gdk_threads_leave ();
  return 0;
}

« Last Edit: January 02, 2011, 09:31:38 PM by JRS »

JRS

  • Guest
Re: MTSB
« Reply #14 on: January 01, 2011, 09:27:34 PM »
Success!

I now have GTK-Server working in a multi-threaded environment.

GNOME GDK Threads: Cross platform g_threads programming API

Code: Text
  1. INCLUDE curl.bas
  2.  
  3. ch = curl::init()
  4.  
  5. curl::option(ch,"NOBODY")
  6. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtgtk.sb")
  7. curl::perform(ch)
  8.  
  9. curl::option(ch,"NOBODY")
  10. curl::option(ch,"URL","http://127.0.0.1:8080/mtsb/mtgtk.sb")
  11. curl::perform(ch)
  12.  
  13. curl::finish(ch)
  14.  

Code: Text
  1. PRINT "Status: 200\nContent-Type: text/html\n\n"
  2.  
  3. DECLARE SUB DLL ALIAS "_idll" LIB "iDLL-Server"
  4. DECLARE SUB DEFINE ALIAS "_idll_define" LIB "iDLL-Server"
  5.  
  6. DEFINE "gtk_init NONE NONE 2 NULL NULL"
  7. DEFINE "gtk_window_new delete-event WIDGET 1 INT"
  8. DEFINE "gtk_window_set_title NONE NONE 2 WIDGET STRING"
  9. DEFINE "gtk_table_new NONE WIDGET 3 INT INT BOOL"
  10. DEFINE "gtk_container_add NONE NONE 2 WIDGET WIDGET"
  11. DEFINE "gtk_button_new_with_label clicked WIDGET 1 STRING"
  12. DEFINE "gtk_table_attach_defaults NONE NONE 6 WIDGET WIDGET INT INT INT INT"
  13. DEFINE "gtk_widget_show_all NONE NONE 1 WIDGET"
  14. DEFINE "gtk_server_callback NONE STRING 1 STRING"
  15. DEFINE "gtk_widget_destroy NONE NONE 1 WIDGET"
  16. DEFINE "g_thread_init NONE NONE 1 NULL"
  17. DEFINE "gdk_threads_init NONE NONE 0"
  18. DEFINE "gdk_threads_enter NONE NONE 0"
  19. DEFINE "gdk_threads_leave NONE NONE 0"
  20.  
  21. DLL("g_thread_init NULL")
  22. DLL("gdk_threads_init")
  23. DLL("gdk_threads_enter")
  24. DLL("gtk_init NULL NULL")
  25. win = DLL("gtk_window_new 0")
  26. DLL("gtk_window_set_title " & win & " \"SB Gtk\"")
  27. tbl = DLL("gtk_table_new 10 10 1")
  28. DLL("gtk_container_add " & win & " " & tbl)
  29. but = DLL("gtk_button_new_with_label \"Quit\"")
  30. DLL("gtk_table_attach_defaults " & tbl & " " & but & " 5 9 5 9")
  31. DLL("gtk_widget_show_all " & win)
  32.  
  33. REPEAT
  34.   event = DLL("gtk_server_callback WAIT")
  35. UNTIL event = win OR event = but
  36.  
  37. DLL("gtk_widget_destroy " & win)
  38. DLL("gtk_server_callback 2")
  39. DLL("gdk_threads_leave")
  40.  
  41. END
  42.  

Both Gtk windows are independent of each other but can share common variables (read/write lockable) provided by the MT module.

Please don't try to use this example for a production environment until the threading in properly done at the C level and compensates for how GTK-Server handles it's event loop processing. This example is just a scripted proof of concept to show that Gtk can run in a threaded environment.
« Last Edit: January 03, 2011, 12:54:30 AM by JRS »