The information below is now out-of-date. There is a much better example of using gtk-server as an editor at this page (6/feb/2012): here

I'm a Windows user, and want to use Falcon - but did it have GUI abilities? Yes, there is a GTK binding. I tried this, and found errors in this. Maybe they were my fault, but they could not be resolved via the falcon message board.

Anyway, by chance I came across gtk-server, which works by running gtk as a parallel process. Your program sends strings of text to gtk-server, and gets back messages as strings of text. This seems to work with Falcon, after coding a little function to read and send data streams to a process.

Below is a trivial text editor for Windows - it is not for serious work, more a proof of concept. To run it, you need falcon of course, and also the gtk-server software, which installs under Windows very simply (I'm not sure about the ease of Linux installing). As a bonus, there is a button which runs ths falcon interpreter on the code you are editing.

Screenshot

/*
editor1p0.fal -Windows version - using gtk-server -  http://gtk-server.org/
mikeparr at live dot com  - Dec 5th 2011
To run this, you need to download and install gtk-server
 The site also has docs and examples.
 
 There is a screenshot and more info on this program at:
 http://www.mikeparr.info/fade/falcongtk.html
 
 Please note that I am not an expert on GTK or falcon!  I present this
 as a demo/example only.

(****NOTE: the following info about local config file is not used now)
  I added another GTK function, using a local cfg file.  To incorporate this:
   1. find the gtk-server.cfg file (it comes with the install) and insert the line:
INCLUDE = gtk-server-extra.cfg
       (you will see a comment about INCLUDE in the file)
                                                                       
   2.  In the same folder as this program, create a new file named:  gtk-server-extra.cfg
        containing this line:                                     
FUNCTION_NAME = gtk_box_set_homogeneous, NONE, NONE, 2, WIDGET, BOOL

If you can't be bothered to do 1 and 2 above, delete the 'set_homogenous' line below - it still runs.
 (**** end of local config note)
 
 
 ---------------------------------------------------------------------------- */

load process  //for the GTK() function                                              

gtkProcess=Process("gtk-server -stdin")  //run gtk-srver in parallel (see gtk-server site)
readFromGTK = gtkProcess.getOutput()     //get the streams  -  see in GTK() function below
writeToGTK = gtkProcess.getInput()
//-----------------------------------------------------------------------------

currentName=""   // global

//********** begin to build the gui ******************************************* 
GTK("gtk_init NULL NULL")  
win=GTK("gtk_window_new 0")    //main window, and get ptr to it                                                     

setTitle(win, currentName)
GTK ("gtk_window_set_position "+win +" 1"    )
GTK( "gtk_widget_set_size_request "+win+" 500 300" )

messageDialog=0   //for the pop-up  - needs to be global, I think

// make a vertical box
vbox = GTK("gtk_vbox_new 0 0")

//GTK("gtk_box_set_homogeneous "+vbox+" 0")   //this gtk command added to local config - but not used now
GTK ("gtk_container_add "+ win +" "+ vbox)    //add vbox to win
//-----------------------------------------------------------------------------

//whole menu bar
menuBar = GTK("gtk_menu_bar_new") 

//create one menu list
fileMenu = GTK("gtk_menu_new")
file = GTK("gtk_menu_item_new_with_label File")          //name at top of menu - not really a menu item!
GTK("gtk_menu_shell_append "+menuBar + " " + file)       // add file menu to menubar
GTK("gtk_menu_item_set_submenu " +file+" "+ fileMenu)    // add filemenu to file

//add item to  a menu list   nb I add to fileMenu
openMenu = GTK("gtk_menu_item_new_with_label \"Open Menu item\" ") // menu item!(nb \" if it contains spaces
GTK("gtk_menu_shell_append "+fileMenu + " " + openMenu);           // low level actual item!

//add item to a menu list
saveAsMenu = GTK("gtk_menu_item_new_with_label \"Save As Menu item\" ")  // menu item!
GTK("gtk_menu_shell_append "+fileMenu + " " + saveAsMenu)                //low level actual item!

//add item to a menu list
saveMenu = GTK("gtk_menu_item_new_with_label \"Save  Menu item\" ")   // menu item!
GTK("gtk_menu_shell_append "+fileMenu + " " + saveMenu)               //low level actual item!


//create one menu list
editMenu = GTK("gtk_menu_new")
edit = GTK("gtk_menu_item_new_with_label Edit")             //name at top of menu - not a menu item!
GTK("gtk_menu_shell_append "+menuBar + " " + edit)
GTK("gtk_menu_item_set_submenu " +edit+" "+ editMenu)

//add item to a menu list
copyMenu = GTK("gtk_menu_item_new_with_label \"copy Menu item\" ") // menu item!
GTK("gtk_menu_shell_append "+editMenu + " " + copyMenu)            // low level actual item!

//-----------hbox of buttons -------------------------------------------------
hbox=GTK( "gtk_hbox_new 0 0")

buttonRun = GTK("gtk_button_new_from_stock \"Run It\"")        //or with label is ok
buttonSpare = GTK("gtk_button_new_from_stock new/help")
GTK("gtk_box_pack_start "+hbox+" "+  buttonRun+"  0 0 1")      //was 1 1 1
GTK("gtk_box_pack_start "+hbox+" "+  buttonSpare+"  0 0 1")
GTK( "gtk_widget_set_size_request "+buttonRun+" 77 33" )       //needed?
GTK( "gtk_widget_set_size_request "+buttonSpare+" 77 33" )


//------------------textview in a scroller,  with associated buffer ------------
buffer=GTK("gtk_text_buffer_new")
myTextview=GTK("gtk_text_view_new_with_buffer "+buffer)
GTK("gtk_text_view_set_wrap_mode "+myTextview+" 2")    //WORD_WRAP = enum 2
scrolledWindow=GTK("gtk_scrolled_window_new NULL NULL")
GTK("gtk_container_add " + scrolledWindow+ " "+ myTextview)
// there are several settings for textview, which i am ignoring here 
//------------------------------------------------------------------------------  

// build the whole screen
GTK("gtk_box_pack_start "+vbox+" "+ menuBar+" 0 0 1");         //add menu to top of vbox
GTK( "gtk_box_pack_start "+vbox +" "+hbox+" 0 0 1")            //  buttons
GTK( "gtk_box_pack_start "+vbox +" "+scrolledWindow+" 1 1 1")  //textview

// 'variables'  (objects) for textview buffer
startiter = GTK("gtk_frame_new NULL")
enditer = GTK("gtk_frame_new NULL")


GTK("gtk_widget_show_all "+win)  //show the whole thing
//************* end of building GUI ******************************************



//============================================================================
//-------------------------events---------------------------------------------
//maybe not the best way to structure event code, but seems to work for this simple program
//NB does NOT  let me call more than 1 event generators in one function
loop
   event = GTK("gtk_server_callback WAIT")
   
   if event == buttonRun: doRunMenuClicked()
   if event == buttonSpare: spareClicked()   
   if event == openMenu: doOpenMenuClicked()   
   if event == saveAsMenu:doSaveAsMenuClicked()
   if event == saveMenu:doSaveMenuClicked()
   if event == copyMenu:copyClicked() 
   if event == messageDialog: GTK("gtk_widget_destroy " +messageDialog) //OK or 'closed' clicked

end   (event == win)

//here when main window is closed by user
doSaveMenuClicked()   // save on exit 
GTK("gtk_exit 0"  )  //end the program

//-----------------------end events----------------------------------------
//=========================================================================


function doRunMenuClicked()
   screen=getScreen(myTextview)
   if not((currentName=="") and (screen=="")) //need some text and a file name
      writeCurrentFile(screen)    //save it
      //nb should create bat in same dir as user's program,nd ensure it is workdir for it
      userDir=filePath(strReplace(currentName, "\\", "/"))  //WINDOWS - but this should have no efect on linux
      f=OutputStream(userDir+"/"+"dorun.bat")    //but where?  -in user's dir (nb no PAUSE needed
      f.writeText(@"falcon.exe \"$currentName\"\r\n")  //WINDOWS   \r
      f.flush()
      f.close()
      
      myDir=dirCurrent()
      dirChange(userDir)   //for program i am about to run
      system("dorun.bat")
      dirChange(myDir)  //back to editor
   end
end

//----------------------------------------------------------------------------

function doOpenMenuClicked()
   global currentName
   //file chooser
   //nb the C description in gtk docs used constants: GTK_STOCK_CANCEL and OPEN.
   //  I looked up their defined val, and used that (e.g  "gtk-open")
   
   //I look at 4 cases:
   screen=getScreen(myTextview)   //mtextview not assigned, hence global value used

   if(currentName=="") and (screen=="")
      opendialog=GTK(@"gtk_file_chooser_dialog_new open-file $win GTK_FILE_CHOOSER_ACTION_OPEN  gtk-cancel GTK_RESPONSE_CANCEL gtk-open GTK_RESPONSE_ACCEPT  NULL")   
      response=GTK(@"gtk_dialog_run $opendialog")                                                                                                      
      //am not setting any chooser options - though I could
      
      if (response== "-3")    //  GTK_RESPONSE_ACCEPT is -3) nb need   "" round -3      
         currentName = GTK(@"gtk_file_chooser_get_filename $opendialog")
         setTitle(win, currentName)
         textToScreen(readAllFile(currentName)) 
      end
      //other responses could be :  close, cancel   -4, -6
      //nb if cancelled, currentname is unchanged
      GTK(@"gtk_widget_destroy $opendialog")
      
   elif (currentName=="") and (screen != "")   //text entered, but no name chosen
      opendialog=GTK(@"gtk_file_chooser_dialog_new open-file $win GTK_FILE_CHOOSER_ACTION_OPEN  gtk-cancel GTK_RESPONSE_CANCEL gtk-open GTK_RESPONSE_ACCEPT  NULL")   
      response=GTK(@"gtk_dialog_run $opendialog")                                                                                                      
      if (response== "-3")    
         currentName = GTK(@"gtk_file_chooser_get_filename $opendialog")
         setTitle(win, currentName)
         textToScreen(readAllFile(currentName))
      end
      GTK(@"gtk_widget_destroy $opendialog")
      
   elif (currentName != "") and (screen  == "")    //got a name, but no text
      opendialog=GTK(@"gtk_file_chooser_dialog_new open-file $win GTK_FILE_CHOOSER_ACTION_OPEN  gtk-cancel GTK_RESPONSE_CANCEL gtk-open GTK_RESPONSE_ACCEPT  NULL")   
      response=GTK(@"gtk_dialog_run $opendialog")                                                                                                          
      if (response== "-3")         
         currentName = GTK(@"gtk_file_chooser_get_filename $opendialog")
         setTitle(win, currentName)
         textToScreen(readAllFile(currentName))
      end
      
      GTK(@"gtk_widget_destroy $opendialog")
      
   elif (currentName!="") and (screen != "")   //normal case
      writeCurrentFile(screen)    //save without asking
      opendialog=GTK(@"gtk_file_chooser_dialog_new open-file $win GTK_FILE_CHOOSER_ACTION_OPEN  gtk-cancel GTK_RESPONSE_CANCEL gtk-open GTK_RESPONSE_ACCEPT  NULL")   
      response=GTK(@"gtk_dialog_run $opendialog")                                                                                                          
      if (response== "-3")     
         currentName = GTK(@"gtk_file_chooser_get_filename $opendialog")
         setTitle(win, currentName)
         textToScreen(readAllFile(currentName))
      end
      
      GTK(@"gtk_widget_destroy $opendialog")
      
   else 
      buildDialog("Error in editor -in Open section")
   end
   
end
//-----------------------------------------------------------------------------

function readAllFile(fName)  //return all text in file f   //bug
   
   inText=strBuffer(200000)
   f=InputStream(strReplace(currentName,"\\","/"))   //WINDOWS
   f.readText(inText)    //must be a way to avoid specifying max size?
   f.close()
   return inText   //strbuff to str value - ok?
end   
//---------------------------------------------------------------------------
   
   
  
//------------------------------------------------------------------------------
//format text if needed, and write it to textview
function textToScreen(inText)
   //The text passed to gtkserver must be one string, surrounded by "
   // I cannot pass a raw eol char, and raw " in the string will screw up the overall
   //quoting.  Raw eols get replaced by \n, so an existing \n pair in the text gives problems, as
   //does the  \"  pair.   So I have to replace them.  The following does this.  It is a bit crude, and I think regexps would be better!
   //It has not been thoroughly tested.
   global currentName  
   
   inText1=strReplace(inText,"\r\n","\n")  //WINDOWS only - remove carriage-returns
   
   //using a 2-step approach to avoid 'double' replacing.  First I replace by a magic string
   
   //quote stuff  "  and   \"  
   inText1=strReplace(inText1, "\\\"", "7BSQ")            // do \"  before  "   - longest
   inText1=strReplace(inText1, "\"", "3BSQ") 
   
   //  backslash , double backslash , \b  \r  \t, backslash n
   inText1=strReplace(inText1, "\\\\", "10BACK")         // do \\ before  \   - longest 
   inText1=strReplace(inText1, "\\r", "BACKR")           // do  \r
   inText1=strReplace(inText1, "\\b", "BACKB")           // do  \b
   inText1=strReplace(inText1, "\\t", "BACKT")           // do  \t
 
   inText1=strReplace(inText1, "\\n",  "3BACKN")         //   \n pair 
   inText1=strReplace(inText1, "\\\n",  "XBACKN")        //   \eol    
   inText1=strReplace(inText1, "\\", "6BACK")            // single  \  
      
 //so far, there are no special chars -0 eg \ "  in text.  ---now replace the magic strings
   
   inText1=strReplace(inText1, "6BACK", "\\\\\\" )      // single  \  -do first?
 
 //quote stuff "  and  \"     
   inText1=strReplace(inText1, "3BSQ", "\\\"")          // normal " works-but need to isolate from handling a\", by magic string?
   inText1=strReplace(inText1, "7BSQ", "\\\\\\\"")      //   \"  works in isolation 
   
   //  backslash , double backslash , backslash n
   inText1=strReplace(inText1, "10BACK", "\\\\\\\\\\" )  // do \\ before  \   - longest
   inText1=strReplace(inText1, "BACKR", "\\\\r" )       //   \r
   inText1=strReplace(inText1, "BACKB", "\\\\b" )       //   \b
   inText1=strReplace(inText1, "BACKT", "\\\\t" )       //   \t
  
   inText1=strReplace(inText1, "3BACKN", "\\\n" )       //   \n pair
   inText1=strReplace(inText1, "XBACKN", "\\\\\n")      //   \eol  yes     
 
   inText1=strReplace(inText1, "\n", "\\n")             //replace eol char by 2 chars   \  n yes
   inText1="\"" + inText1 + "\""                        //becuase the whole string is likely to contain spaces
 //  ("about to show=================: \n"+inText1)
    
   GTK(@"gtk_text_buffer_set_text $buffer  $inText1 -1" ) 
end

//------------------------------------------------------------------------------
function doSaveMenuClicked()
   if (currentName=="")
      doSaveAsMenuClicked()
   else
      writeCurrentFile(getScreen(myTextview))
   end
end
//-----------------------------------------------------------------------------

function doSaveAsMenuClicked()
   //no choices here - always display it
   global currentName
   //file chooser-save (nb if you want to type in a file name, there is a small button in the chooser
   savedialog=GTK(@"gtk_file_chooser_dialog_new save-file $win GTK_FILE_CHOOSER_ACTION_SAVE  gtk-cancel GTK_RESPONSE_CANCEL gtk-save GTK_RESPONSE_ACCEPT  NULL") 
   
   response=GTK(@"gtk_dialog_run $savedialog")
   if (response== "-3")     
      currentName = GTK(@"gtk_file_chooser_get_filename $savedialog")
      setTitle(win, currentName)
      writeCurrentFile(getScreen(myTextview))
   end
   //.nb  currentName unchanged if chooser cancelled
   GTK(@"gtk_widget_destroy $savedialog")  
end
//-------------------------------------------------------------------------------

function writeCurrentFile(theText)
   f=OutputStream(currentName)
   f.writeText(theText)
   f.flush()
   f.close()
end

//----------------------------------------------------------------------------


function getScreen(aTV)   //return the text from a textview, properly formatted for file-writing
   //see comments in fileToScreen - but here I need to replace the gtk encoding by actual characters
   
   GTK("gtk_server_enable_c_string_escaping")     //so i get all the text as 1 line
   buffer=GTK("gtk_text_view_get_buffer "+aTV)    //not needed?  not sure if it moves
   
   GTK("gtk_text_buffer_get_bounds "+ buffer +" "+ startiter+ " "+ enditer) //they point to the buffer
   //   GTK("gtk_server_set_c_string_escaping "+"\"\n\"")  //cant get it to work for eol - dont use
   
   allText=GTK("gtk_text_buffer_get_text "+buffer +" "+startiter+" "+enditer+" 0")  // eol, " are escaped"
   GTK("gtk_server_disable_c_string_escaping")
   
   //remove first and last chars (which are ") 
   allText=allText[1:len(allText)-1]
   
   //   \"-y   \n-y    \\-y   "    \   (and \ at eol)
   
   allText=strReplace(allText, "\\\\\\n" ,"BSBSEOL")   //   \ before eol is \\\n  
   allText=strReplace(allText,"\\\\\\\"" , "BSQT")     //    pair  \" -rep by \\\"  in string from gtkserv -yes
   allText=strReplace(allText, "\\\\n" ,"BSNL")        // pair \n rep by   \\n    -yes
   allText=strReplace(allText,"\\\\\\\\" ,"2BS")      // pair \\ rep by \\\\  in string from gtkserv
   allText=strReplace(allText,"\\\"" ,"JUSTQT")       // singl " rep by pair  \" in string from gtkserv -yes
   allText=strReplace(allText, "\\\\" ,"JUSTBS")      // single \  rep by \\ yes    
  //done relacing all by magic
  //am not doing \b \t \r   seems ok!  
   allText=strReplace(allText,"2BS", "\\\\")   
   allText=strReplace(allText, "BSBSEOL" ,"\\\\\n")
              
   allText=strReplace(allText,"\\n", "\r\n")          //do real eols    - yes, here nb WINDOWS
        
   allText=strReplace(allText,"BSQT" ,"\\\"")
   allText=strReplace(allText,"BSNL" ,"\\n") 
   allText=strReplace(allText,"2BS", "\\\\")
   allText=strReplace(allText,"JUSTQT", "\"")
   allText=strReplace(allText,"JUSTBS", "\\")  
   
   return allText
       
end
//----------------------------------------------------------------------------
   
function spareClicked()
   buildDialog("Spare modal? \n Yes, I am.\n"+
   "New file: start typing, do a Save As.   \nThe current file is saved when you open/save-as/exit")    
end
//-----------------------------------------------------------------------------

function copyClicked()
   buildDialog("For copy/paste, use normal control keys, or right mouse button.")  
 end

//------------------------------------------------------------------------------

function setTitle(win, text)
   GTK( "gtk_window_set_title "+ win + " \"Edit - gtk-server(Win):  "+strReplace(text, "\\","/")+ "\"" ) //nb \" if holds spaces
end

//------------------------------------------------------------------------------

function buildDialog(msg)
   global messageDialog   //used in event loop   
   messageDialog= GTK("gtk_message_dialog_new "+ win+ " 0 0 2 '" + msg +"'"+"''")
   GTK(@"gtk_dialog_run $messageDialog")
   
   // GTK("gtk_widget_show_all " + buildDialog("a test----")) //not modal - can show lots at once
   // but might need an assoc array for ptrs to several dialogs
end

//------------------------------------------------------------------------------
//see gtk-server page for what this has to do (basically, send a line, read a line)

function GTK(st)
 //  printl ("sending: "+ st)    //for debugging
   writeToGTK.write(st)
   
   a=readFromGTK.grabLine()
//   printl(" got line: " + a)   //for debugging
   return a
end
//------------------------------------------------------------------------------