This is a sample code to demonstrate the use of
Gtk::io_add_watch(). The main goal of this function
is to handle socket IO without blocking. Socket polling is done
in Gtk::main() loop. You don't need to care about socket polling.
This task is done by Gtk and Glib.
In this example, the
function is used for building an simple IRC client that connect
to an IRC server, identifying itself, connecting to #php-gtk, and
doing some boring things.
Since this is just a use case for the API, there is no user
interface.
There is a package on Gnope server
based on this documentation and class.
Function interface description
Function call parameters
io_id Gtk::io_add_watch($stream, $conditions, $callback)
$conditions parameter description : this is a mask of the following values :
Gtk::IO_IN : stream is ready for reading,
Gtk::IO_OUT : stream is ready for writing,
Gtk::IO_PRI : this is the high priority
channel for stream
Gtk::IO_ERR : stream is in error state,
Gtk::IO_HUP : stream has been
disconnected (sighup)
Gtk::IO_NVAL : (invalid command to socket ?)
You can build mask like this :
$conditions = Gtk::IO_IN|Gtk::IO_OUT.
Callback parameters
function io_callback($gio_channel, $conditions)
$gio_channel is not usable with this
current API implementation ; should be used with
g_io_ functions not implemented.
But IO can be done with standard php functions.
- $conditions : this is effective conditions
that triggered actual event call.
Source code example
<?php
error_reporting(E_ALL);
/*
$conditions : can be a mask of :
Gtk::IO_IN, Gtk::IO_OUT,
Gtk::IO_PRI, Gtk::IO_ERR,
Gtk::IO_HUP, Gtk::IO_NVAL
*/
/**
* Available syntaxes for io_add_watch:
*
$id = io_add_watch($stream, $condition, $callback);
$id = io_add_watch_priority($stream, $condition, $callback,
$priority);
*
*/
class GtkIO {
protected $gio_ids;
protected $fd;
protected $hostname;
protected $port;
protected $errno; # fsocketopen error status
protected $errstr;
function __construct() {
$this->gio_ids = array();
}
function connect($hostname, $port) {
$this->hostname = $hostname;
$this->port = $port;
$this->fd = fsockopen($hostname, $port,
$this->errno, $this->errstr);
if(!$this->fd)
trigger_error("GtkIO::connect : connexion error : "
. "{$this->errstr} ({$this->errno})");
else {
$this->io_setup();
}
return $this->fd;
}
function io_setup() {
$this->gio_ids[Gtk::IO_IN] =
Gtk::io_add_watch($this->fd, Gtk::IO_IN,
array($this, 'io_in'));
$this->gio_ids[Gtk::IO_OUT] =
Gtk::io_add_watch($this->fd, Gtk::IO_OUT,
array($this, 'io_out'));
$this->gio_ids[Gtk::IO_HUP] =
Gtk::io_add_watch($this->fd, Gtk::IO_HUP,
array($this, 'io_hup'));
$this->gio_ids[Gtk::IO_PRI] =
Gtk::io_add_watch($this->fd, Gtk::IO_PRI,
array($this, 'io_pri'));
$this->gio_ids[Gtk::IO_ERR] =
Gtk::io_add_watch($this->fd, Gtk::IO_ERR,
array($this, 'io_err'));
$this->gio_ids[Gtk::IO_NVAL] =
Gtk::io_add_watch($this->fd, Gtk::IO_NVAL,
array($this, 'ion_val'));
}
function write($data) {
return fwrite($this->fd, $data, strlen($data));
}
# very incomplete : need to handle errors ...
function read($size = 2048) {
return fread($this->fd, $size);
}
function eof() {
return feof($this->fd);
}
function io_in($gio_channel, $conditions) {
echo "GtkIO::io_in()\n";
}
function io_out($gio_channel, $conditions) {
echo "GtkIO::io_out()\n";
}
function io_hup($gio_channel, $conditions) {
echo "GtkIO::io_hup()\n";
}
function io_pri($gio_channel, $conditions) {
echo "GtkIO::io_pri()\n";
}
function io_err($gio_channel, $conditions) {
echo "GtkIO::io_err()\n";
}
}
define('GTK_IRC_CONNECTED', 1);
define('GTK_IRC_DISCONNECTED', 2);
class IrcProtocol extends GtkIO {
protected $nick;
protected $user;
protected $channels;
protected $timeout_id;
protected $irc_status;
function __construct() {
$this->timeout = 1000;
$this->timeout_id = Gtk::timeout_add($this->timeout,
array($this, 'timeout'));
$this->irc_status = GTK_IRC_DISCONNECTED;
}
function connect($hostname, $port,
$nick, $user, $channels) {
$status = parent::connect($hostname, $port);
if ($status === false)
die("can't connect to irc server");
$this->irc_status = GTK_IRC_CONNECTED;
$this->nick = $nick;
$this->user = $user;
# initial channels to join;
$this->channels = $channels;
$this->nick($nick);
$this->user($user);
$this->join($channels);
}
function send($str) {
echo "Irc::send($str)\n";
$this->write($str . "\n\r");
}
function timeout() {
static $count = 0;
echo "timeout ...\n";
$count++;
# debug - for gtk_io function monitor
if ($count > 3) {
$this->send_chan('#php-gtk', "I'm going out; bye ...");
$this->disconnect();
return;
}
if ($this->irc_status == GTK_IRC_CONNECTED
&& $this->fd != null)
$this->timeout_id = Gtk::timeout_add($this->timeout,
array($this, 'timeout'));
$this->send_chan('#php-gtk', "hello ; I'm php-gtk-irc bot");
}
function io_in($gio_channel, $conditions) {
$data = $this->read();
echo "$data\n";
}
function io_hup($gio_channel, $conditions) {
echo "irc : disconnected\n";
$this->fd = null;
Gtk::main_quit();
}
function disconnect() {
$this->send('QUIT');
$this->irc_status = GTK_IRC_DISCONNECTED;
Gtk::main_quit();
}
function send_chan($chan, $msg) {
$this->send("PRIVMSG $chan :$msg");
}
function nick($nick) {
$this->send("NICK $nick");
}
function user($user) {
$this->send("USER $user $user $user localhost");
}
function join($channels) {
foreach($channels as $chan)
$this->send("JOIN $chan");
}
}
$irc = new IrcProtocol();
/**
* You'll need to change these parameters
* maybe to connect to chat.freenode.net
*/
$irc->connect(
$server = 'irc.freenode.net',
$port = '6667',
$nick = 'mq-boot',
$user = 'Marc', # put you name here ...
$channels = array('#php-gtk', '#php'));
Gtk::main();
?>
Notes :
- Irc class is incomplete,
GtkIO::read() is very incomplete to.
Links
You can refer to thoses links for a complete documentation. Gtk::io_add_watch() is based on
Glib function io_add_watch().
Note that, although the function is available on all platforms,
this small example doesn't completely work on Windows.
Comentários
io_add_watch
Note:
If the function called by
Gtk::io_add_watchdoes not return true, then Gtk will stop watching:truetells gtk to keep on watchingfalsetells gtk to stop watchingGtkIO Gnope package
A package for this GtkIO class is now available in the gnope repository. This class is now separated in two parts :
GtkIOclassGtkSocketIOclassThis is a nice contribution of Leon Pegg.
refresh Gtk::main_loop() events
This is nearly in the same chapter ; when you have long tasks with blocking or semi-blocking code, you can refresh the event loop like this :
<?phpwhile(1)
{
do_some_work();
# refresh Gtk Window :
while(Gtk::events_pending())
Gtk::main_iteration();
}
?>
Also, you can take into account this function when doing some backgroup task :
Gtk::idle_add()please take care of a little bug
there is a probleme with this script. When FD is closed (hup), you need to close watches with this function :
Gtk::input_remove($id);now, I have a Pipe based
now, I have a Pipe based class fully working here :
http://php-gtk.pastebin.ca/544279
you can use it and adjust to you needs.