Thư viện tri thức trực tuyến
Kho tài liệu với 50,000+ tài liệu học thuật
© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

apress foundations_of gtk plus development 2007 phần 5 pdf
Nội dung xem thử
Mô tả chi tiết
206 CHAPTER 6 ■ USING GLIB
case 0:
gtk_init (&argc, &argv);
setup_app (parent_to_child, child_to_parent, pid);
break;
default:
gtk_init (&argc, &argv);
setup_app (child_to_parent, parent_to_child, pid);
}
gtk_main ();
return 0;
}
/* Set up the GUI aspects of each window and setup IO channel watches. */
static void
setup_app (gint input[],
gint output[],
gint pid)
{
GtkWidget *window, *entry;
GIOChannel *channel_read, *channel_write;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
entry = gtk_entry_new ();
gtk_container_add (GTK_CONTAINER (window), entry);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 200, -1);
gtk_widget_show_all (window);
/* Close the unnecessary pipes for the given process. */
close (input[1]);
close (output[0]);
/* Create read and write channels out of the remaining pipes. */
channel_read = g_io_channel_unix_new (input[0]);
channel_write = g_io_channel_unix_new (output[1]);
if (channel_read == NULL || channel_write == NULL)
g_error ("Error: The GIOChannels could not be created!\n");
/* Watch the read channel for changes. This will send the appropriate data. */
if (!g_io_add_watch (channel_read, G_IO_IN | G_IO_HUP,
iochannel_read, (gpointer) entry))
g_error ("Error: Read watch could not be added to the GIOChannel!\n");
7931ch06.fm Page 206 Wednesday, March 7, 2007 8:52 PM
CHAPTER 6 ■ USING GLIB 207
signal_id = g_signal_connect (G_OBJECT (entry), "changed",
G_CALLBACK (entry_changed),
(gpointer) channel_write);
/* Set the window title depending on the process identifier. */
if (pid == 0)
gtk_window_set_title (GTK_WINDOW (window), "Child Process");
else
gtk_window_set_title (GTK_WINDOW (window), "Parent Process");
}
/* Read the message from the pipe and set the text to the GtkEntry. */
static gboolean
iochannel_read (GIOChannel *channel,
GIOCondition condition,
GtkEntry *entry)
{
GIOStatus ret_value;
gchar *message;
gsize length;
/* The pipe has died unexpectedly, so exit the application. */
if (condition & G_IO_HUP)
g_error ("Error: The pipe has died!\n");
/* Read the data that has been sent through the pipe. */
ret_value = g_io_channel_read_line (channel, &message, &length, NULL, NULL);
if (ret_value == G_IO_STATUS_ERROR)
g_error ("Error: The line could not be read!\n");
/* Synchronize the GtkEntry text, blocking the changed signal. Otherwise, an
* infinite loop of communication would ensue. */
g_signal_handler_block ((gpointer) entry, signal_id);
message[length-1] = 0;
gtk_entry_set_text (entry, message);
g_signal_handler_unblock ((gpointer) entry, signal_id);
return TRUE;
}
/* Write the new contents of the GtkEntry to the write IO channel. */
static void
entry_changed (GtkEditable *entry,
GIOChannel *channel)
7931ch06.fm Page 207 Wednesday, March 7, 2007 8:52 PM
208 CHAPTER 6 ■ USING GLIB
{
gchar *text;
gsize length;
GIOStatus ret_value;
text = g_strconcat (gtk_entry_get_text (GTK_ENTRY (entry)), "\n", NULL);
/* Write the text to the channel so that the other process will get it. */
ret_value = g_io_channel_write_chars (channel, text, -1, &length, NULL);
if (ret_value = G_IO_STATUS_ERROR)
g_error ("Error: The changes could not be written to the pipe!\n");
else
g_io_channel_flush (channel, NULL);
}
Setting Up IO Channels
If you are working on a UNIX-like machine, you can use the pipe() function to create new file
descriptors. In Listing 6-9, two pairs of pipes are set up: one for sending messages from the parent to the child and one for sending messages in the other direction. Two GIOChannels can then
be created from these file descriptors by calling the following function on each.
After the pipes are created, the application is forked with fork(). If the fork is successful,
the application is set up for both the child and the parent process.
Within setup_app(), we begin by closing the pipes that are not needed by the child or parent applications with close(). Each process will only need one read and one write pipe in order
to send and receive messages.
Next, we use the two remaining pipes in each application and set up a GIOChannel for each.
We will use channel_read to receive data from the other process and channel_write to send the
new content of the GtkEntry.
channel_read = g_io_channel_unix_new (input[0]);
channel_write = g_io_channel_unix_new (output[1]);
After initializing your IO channels, you need to set up a watch on channel_read. The watch
will monitor the channel for the specified events, which is setup with g_io_add_watch().
guint g_io_add_watch (GIOChannel *channel,
GIOCondition condition,
GIOFunc func,
gpointer data);
7931ch06.fm Page 208 Wednesday, March 7, 2007 8:52 PM
CHAPTER 6 ■ USING GLIB 209
The second parameter of g_io_add_watch() adds one or more events that should be
watched. You need to make sure to set up the correct conditions with each channel. You will
never get a G_IO_IN event from a channel used for writing data, so monitoring for that event is
useless. Possible values for the GIOCondition enumeration follow; these can be piped to the
condition parameter of g_io_add_watch():
• G_IO_IN: Read data is pending.
• G_IO_OUT: Data can be written without the worry of blocking.
• G_IO_PRI: Read data is pending and urgent.
• G_IO_ERR: An error has occurred.
• G_IO_HUP: The connection has been hung up or broken.
• G_IO_NVAL: An invalid request has occurred because the file descriptor is not open.
When one of the specified conditions occurs, the GIOFunc callback function is called. The
last parameter gives data that will be passed to the callback function. IO channel callback functions receive three parameters: the GIOChannel, the condition that occurred, and the data
passed from g_io_add_watch(). TRUE should always be returned from the callback function
unless you want it to be removed. The function prototype follows:
gboolean (*GIOFunc) (GIOChannel *source, GIOCondition condition, gpointer data);
Reading from and writing to a GIOChannel is done in the same manner regardless of whether
it is a file or a pipe. Therefore, the g_io_channel_read_(*) and g_io_channel_write_*() functions covered in the previous section can still be used.
Many of the GIOChannel functions provide two ways to check for errors. The first is the
GError structure that we have used in past chapters. Secondly, many functions return a
GIOStatus value, which will report one of the following four values:
• G_IO_STATUS_ERROR: Some type of error has occurred. You should still track errors even if
you are checking for this value.
• G_IO_STATUS_NORMAL: The action was successfully completed.
• G_IO_STATUS_EOF: The end of the file has been reached.
• G_IO_STATUS_AGAIN: Resources are temporarily unavailable. You should try again later.
7931ch06.fm Page 209 Wednesday, March 7, 2007 8:52 PM
210 CHAPTER 6 ■ USING GLIB
Depending on the GIOStatus value, you should either continue or give an error message.
The only exception is G_IO_STATUS_AGAIN, in which case you should return to poll() in the
main loop and wait for the file descriptor to become ready.
To send the data to the read buffer, you need to flush the write buffer of the GIOChannel
with g_io_channel_flush(). This function, along with all of the functions in this section, can
cause an error of the type GIOChannelError.
GIOStatus g_io_channel_flush (GIOChannel *channel,
GError **error);
Spawning Processes
The GIOChannel example in the previous section used pipe() and fork() to set up the communication between the applications. However, this example is not cross-platform, because some
commands will not be supported on Microsoft Windows.
To spawn processes in a way supported by multiple platforms, GLib provides three functions. Since all three work in a similar way, we will only talk about the following function,
g_spawn_async_with_pipes():
gboolean g_spawn_async_with_pipes (const gchar *working_directory,
gchar **argv,
gchar **envp,
GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer data,
GPid *child_pid,
gint *standard_input,
gint *standard_output,
gint *standard_error,
GError **error);
This function asynchronously runs a child program, which means that the program will
continue to run even if the child has not exited. The first parameter specifies the working directory for the child process or NULL to set it as the parent’s working directory.
The argv list is a NULL-terminated array of strings. The first string in this list is the name of the
application, followed by any additional parameters. This application must be a full path unless
you use the G_SPAWN_SEARCH_PATH flag, which will be shown later. Another NULL-terminated array
of strings is envp, each in the form KEY=VALUE. These will be set as the child’s environment
variables.
7931ch06.fm Page 210 Wednesday, March 7, 2007 8:52 PM
CHAPTER 6 ■ USING GLIB 211
You can then specify one or more of the following GSpawnFlags:
• G_SPAWN_LEAVE_DESCRIPTORS_OPEN: The child will inherit the open file descriptors of the
parent. If this flag is not set, all file descriptors except the standard input, output, and
error will be closed.
• G_SPAWN_DO_NOT_REAP_CHILD: Stop the child from automatically becoming reaped. If you
do not call waitpid() or handle SIGCHLD, it will become a zombie.
• G_SPAWN_SEARCH_PATH: If this flag is set, argv[0] will be searched for in the user’s path if it
is not an absolute location.
• G_SPAWN_STDOUT_TO_DEV_NULL: Discard the standard output from the child. If this flag is
not set, it will go to the same location as the parent’s standard output.
• G_SPAWN_STDERR_TO_DEV_NULL: Discard the standard error from the child.
• G_SPAWN_CHILD_INHERITS_STDIN: If this flag is not set, the standard input for the child is
attached to /dev/null. You can use this flag so the child will inherit the standard input of
the parent.
• G_SPAWN_FILE_AND_ARGV_ZERO: Use the first argument as the executable and only pass the
remaining strings as the actual arguments. If this flag is not set, argv[0] will also be
passed to the executable.
The next parameter of g_spawn_async_with_pipes() is the GSpawnChildSetupFunc callback
function that will be run after GLib sets up pipes but before calling exec(). This function
accepts the data parameter from g_spawn_async_with_pipes().
The next four parameters allow you to retrieve information about the new child process.
These are the child’s process identifier, standard input, standard output, and standard error.
Any of these four parameters can be set to NULL if you want to ignore it.
If the application was successfully launched, g_spawn_async_with_pipes() will return
TRUE. Otherwise, the error will be set under the GSpawnError domain, and it will return FALSE.
When you are finished with a GPid, you should use g_spawn_close_pid() to close it. This is
especially important when spawning processes on Microsoft Windows.
void g_spawn_close_pid (GPid pid);
7931ch06.fm Page 211 Wednesday, March 7, 2007 8:52 PM