?? gmain.c
字號(hào):
#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <stdarg.h>#include <malloc.h>#include <string.h>#include <limits.h>#include <sys/time.h>#include <time.h>#include <sys/types.h>#include <sys/wait.h>#include <fcntl.h>#include <sys/stat.h>#include <sys/mman.h>#include <sys/file.h>#include <ctype.h>#include <dlfcn.h>#include <gmain.h>struct timeout { guint id; guint interval; struct timeval expiration; gpointer data; GSourceFunc function;};struct _GIOChannel { int fd; int ref_count; gboolean closed; gboolean close_on_unref;};struct child_watch { guint id; GPid pid; GChildWatchFunc function; gpointer user_data;};struct _GMainContext { guint next_id; glong next_timeout; GSList *timeouts; GSList *proc_timeouts; gboolean timeout_lock; GSList *io_watches; GSList *proc_io_watches; gboolean io_lock; GSList *child_watches; GSList *proc_child_watches; gboolean child_lock;};struct _GMainLoop { gboolean is_running; GMainContext *context;};GIOError g_io_channel_read(GIOChannel *channel, gchar *buf, gsize count, gsize *bytes_read){ int fd = channel->fd; gssize result; if (channel->closed) return G_IO_STATUS_ERROR; /* At least according to the Debian manpage for read */ if (count > SSIZE_MAX) count = SSIZE_MAX;retry: result = read (fd, buf, count); if (result < 0) { *bytes_read = 0; switch (errno) {#ifdef EINTR case EINTR: goto retry;#endif#ifdef EAGAIN case EAGAIN: return G_IO_STATUS_AGAIN;#endif default: return G_IO_STATUS_ERROR; } } *bytes_read = result; return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;}GIOError g_io_channel_write(GIOChannel *channel, const gchar *buf, gsize count, gsize *bytes_written){ int fd = channel->fd; gssize result; if (channel->closed) return G_IO_STATUS_ERROR; /* At least according to the Debian manpage for read */ if (count > SSIZE_MAX) count = SSIZE_MAX;retry: result = write(fd, buf, count); if (result < 0) { *bytes_written = 0; switch (errno) {#ifdef EINTR case EINTR: goto retry;#endif#ifdef EAGAIN case EAGAIN: return G_IO_STATUS_AGAIN;#endif default: return G_IO_STATUS_ERROR; } } *bytes_written = result; return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;}void g_io_channel_close(GIOChannel *channel){ if (!channel || channel->closed) return; close(channel->fd); channel->closed = TRUE;}void g_io_channel_unref(GIOChannel *channel){ if (!channel) return; if (--channel->ref_count > 0) return; if (channel->close_on_unref && channel->fd >= 0) g_io_channel_close(channel); g_free(channel);}GIOChannel *g_io_channel_ref(GIOChannel *channel){ channel->ref_count++; return channel;}GIOChannel *g_io_channel_unix_new(int fd){ GIOChannel *channel; channel = g_new0(GIOChannel, 1); channel->fd = fd; channel->ref_count = 1; return channel;}void g_io_channel_set_close_on_unref(GIOChannel *channel, gboolean do_close){ channel->close_on_unref = do_close;}gint g_io_channel_unix_get_fd(GIOChannel *channel){ if (channel->closed) return -1; return channel->fd;}struct io_watch { guint id; GIOChannel *channel; gint priority; GIOCondition condition; short *revents; GIOFunc func; gpointer user_data; GDestroyNotify destroy;};static GMainContext *default_context = NULL;static void watch_free(struct io_watch *watch){ if (watch->destroy) watch->destroy(watch->user_data); g_io_channel_unref(watch->channel); g_free(watch);}static GMainContext *g_main_context_default(){ if (default_context) return default_context; default_context = g_new0(GMainContext, 1); default_context->next_timeout = -1; default_context->next_id = 1; return default_context;}static gboolean g_io_remove_watch(GMainContext *context, guint id){ GSList *l; struct io_watch *w; for (l = context->io_watches; l != NULL; l = l->next) { w = l->data; if (w->id != id) continue; context->io_watches = g_slist_remove(context->io_watches, w); watch_free(w); return TRUE; } for (l = context->proc_io_watches; l != NULL; l = l->next) { w = l->data; if (w->id != id) continue; context->proc_io_watches = g_slist_remove(context->proc_io_watches, w); watch_free(w); return TRUE; } return FALSE;}static gboolean g_timeout_remove(GMainContext *context, const guint id){ GSList *l; struct timeout *t; l = context->timeouts; while (l) { t = l->data; l = l->next; if (t->id != id) continue; context->timeouts = g_slist_remove(context->timeouts, t); g_free(t); return TRUE; } l = context->proc_timeouts; while (l) { t = l->data; l = l->next; if (t->id != id) continue; context->proc_timeouts = g_slist_remove(context->proc_timeouts, t); g_free(t); return TRUE; } return FALSE;}int watch_prio_cmp(struct io_watch *w1, struct io_watch *w2){ return w1->priority - w2->priority;}#define watch_list_add(l, w) g_slist_insert_sorted((l), (w), (GCompareFunc) watch_prio_cmp)guint g_io_add_watch_full(GIOChannel *channel, gint priority, GIOCondition condition, GIOFunc func, gpointer user_data, GDestroyNotify notify){ struct io_watch *watch; GMainContext *context = g_main_context_default(); watch = g_new(struct io_watch, 1); watch->id = context->next_id++; watch->channel = g_io_channel_ref(channel); watch->priority = priority; watch->condition = condition; watch->func = func; watch->user_data = user_data; watch->destroy = notify; if (context->io_lock) context->proc_io_watches = watch_list_add(context->proc_io_watches, watch); else context->io_watches = watch_list_add(context->io_watches, watch); return watch->id;}guint g_io_add_watch(GIOChannel *channel, GIOCondition condition, GIOFunc func, gpointer user_data){ return g_io_add_watch_full(channel, 0, condition, func, user_data, NULL);}GMainLoop *g_main_loop_new(GMainContext *context, gboolean is_running){ GMainLoop *ml; if (!context) context = g_main_context_default(); ml = g_new0(GMainLoop, 1); ml->context = context; ml->is_running = is_running; return ml;}static void timeout_handlers_prepare(GMainContext *context){ GSList *l; struct timeval tv; glong msec, timeout = LONG_MAX; gettimeofday(&tv, NULL); for (l = context->timeouts; l != NULL; l = l->next) { struct timeout *t = l->data; /* calculate the remainning time */ msec = (t->expiration.tv_sec - tv.tv_sec) * 1000 + (t->expiration.tv_usec - tv.tv_usec) / 1000; if (msec < 0) msec = 0; timeout = MIN_TIMEOUT(timeout, msec); } /* set to min value found or NO timeout */ context->next_timeout = (timeout != LONG_MAX ? timeout : -1);}static int ptr_cmp(const void *t1, const void *t2){ return t1 - t2;}static void timeout_handlers_check(GMainContext *context){ struct timeval tv; gettimeofday(&tv, NULL); context->timeout_lock = TRUE; while (context->timeouts) { struct timeout *t = context->timeouts->data; glong secs, msecs; gboolean ret; if (timercmp(&tv, &t->expiration, <)) { context->timeouts = g_slist_remove(context->timeouts, t); context->proc_timeouts = g_slist_append(context->proc_timeouts, t); continue; } ret = t->function(t->data); /* Check if the handler was removed/freed by the callback * function */ if (!g_slist_find_custom(context->timeouts, t, ptr_cmp)) continue; context->timeouts = g_slist_remove(context->timeouts, t); if (!ret) { g_free(t); continue; } /* update the next expiration time */ secs = t->interval / 1000; msecs = t->interval - secs * 1000; t->expiration.tv_sec = tv.tv_sec + secs; t->expiration.tv_usec = tv.tv_usec + msecs * 1000; if (t->expiration.tv_usec >= 1000000) { t->expiration.tv_usec -= 1000000; t->expiration.tv_sec++; } context->proc_timeouts = g_slist_append(context->proc_timeouts, t); } context->timeouts = context->proc_timeouts; context->proc_timeouts = NULL; context->timeout_lock = FALSE;}void g_main_loop_run(GMainLoop *loop){ int open_max = sysconf(_SC_OPEN_MAX); struct pollfd *ufds; GMainContext *context = loop->context; ufds = g_new(struct pollfd, open_max); loop->is_running = TRUE; while (loop->is_running) { int nfds; GSList *l; struct io_watch *w; for (nfds = 0, l = context->io_watches; l != NULL; l = l->next, nfds++) { w = l->data; ufds[nfds].fd = w->channel->fd; ufds[nfds].events = w->condition; ufds[nfds].revents = 0; w->revents = &ufds[nfds].revents; } /* calculate the next timeout */ timeout_handlers_prepare(context); if (poll(ufds, nfds, context->next_timeout) < 0) continue; context->io_lock = TRUE; while (context->io_watches) { gboolean ret; w = context->io_watches->data; if (!*w->revents) { context->io_watches = g_slist_remove(context->io_watches, w); context->proc_io_watches = watch_list_add(context->proc_io_watches, w); continue; } ret = w->func(w->channel, *w->revents, w->user_data); /* Check if the watch was removed/freed by the callback * function */ if (!g_slist_find_custom(context->io_watches, w, ptr_cmp)) continue; context->io_watches = g_slist_remove(context->io_watches, w); if (!ret) { watch_free(w); continue; } context->proc_io_watches = watch_list_add(context->proc_io_watches, w); } context->io_watches = context->proc_io_watches; context->proc_io_watches = NULL; context->io_lock = FALSE; /* check expired timers */ timeout_handlers_check(loop->context); } g_free(ufds);}void g_main_loop_quit(GMainLoop *loop){ loop->is_running = FALSE;}void g_main_loop_unref(GMainLoop *loop){ if (!loop->context) return; g_slist_foreach(loop->context->io_watches, (GFunc)watch_free, NULL); g_slist_free(loop->context->io_watches); g_slist_foreach(loop->context->timeouts, (GFunc)g_free, NULL); g_slist_free(loop->context->timeouts); g_free(loop->context); loop->context = NULL;}guint g_timeout_add(guint interval, GSourceFunc function, gpointer data){ GMainContext *context = g_main_context_default(); struct timeval tv; guint secs; guint msecs; struct timeout *t; t = g_new0(struct timeout, 1); t->interval = interval; t->function = function; t->data = data; gettimeofday(&tv, NULL); secs = interval /1000; msecs = interval - secs * 1000; t->expiration.tv_sec = tv.tv_sec + secs; t->expiration.tv_usec = tv.tv_usec + msecs * 1000; if (t->expiration.tv_usec >= 1000000) { t->expiration.tv_usec -= 1000000; t->expiration.tv_sec++; } /* attach the timeout the default context */ t->id = context->next_id++; if (context->timeout_lock) context->proc_timeouts = g_slist_prepend(context->proc_timeouts, t); else context->timeouts = g_slist_prepend(context->timeouts, t); return t->id;}guint g_idle_add(GSourceFunc function, gpointer data){ return g_timeout_add(1, function, data);}/* GError */GError* g_error_new_literal(GQuark domain, gint code, const gchar *message){ GError *err; err = g_new(GError, 1); err->domain = domain; err->code = code; err->message = g_strdup(message); return err;}void g_set_error(GError **err, GQuark domain, gint code, const gchar *format, ...){ gchar msg[1024]; va_list ap; if (!err) return; va_start(ap, format); vsnprintf(msg, sizeof(msg) - 1, format, ap); va_end(ap); *err = g_error_new_literal(domain, code, msg);}void g_error_free(GError *err){ g_free(err->message); g_free(err);}/* Spawning related functions */static int child_watch_pipe[2] = { -1, -1 };static void sigchld_handler(int signal){ int ret; ret = write(child_watch_pipe[1], "B", 1);}static gboolean child_watch_remove(GMainContext *context, guint id){ GSList *l; struct child_watch *w; for (l = context->child_watches; l != NULL; l = l->next) { w = l->data; if (w->id != id) continue; context->child_watches = g_slist_remove(context->child_watches, w); g_free(w); return TRUE; } for (l = context->proc_child_watches; l != NULL; l = l->next) { w = l->data; if (w->id != id) continue; context->proc_child_watches = g_slist_remove(context->proc_child_watches, w); g_free(w); return TRUE; } return FALSE;}static gboolean child_watch(GIOChannel *io, GIOCondition cond, gpointer user_data){ int ret; char b[20]; GMainContext *context = g_main_context_default(); ret = read(child_watch_pipe[0], b, 20); context->child_lock = TRUE; while (context->child_watches) { gint status; struct child_watch *w = context->child_watches->data; if (waitpid(w->pid, &status, WNOHANG) <= 0) { context->child_watches = g_slist_remove(context->child_watches, w); context->proc_child_watches = watch_list_add(context->proc_child_watches, w); continue; } w->function(w->pid, status, w->user_data); /* Check if the callback already removed us */ if (!g_slist_find(context->child_watches, w)) continue; context->child_watches = g_slist_remove(context->child_watches, w); g_free(w); } context->child_watches = context->proc_child_watches; context->proc_child_watches = NULL; context->child_lock = FALSE; return TRUE;}static void init_child_pipe(void){ struct sigaction action; GIOChannel *io; if (pipe(child_watch_pipe) < 0) { fprintf(stderr, "Unable to initialize child watch pipe: %s (%d)\n", strerror(errno), errno); abort(); } fcntl(child_watch_pipe[1], F_SETFL, O_NONBLOCK | fcntl(child_watch_pipe[1], F_GETFL)); action.sa_handler = sigchld_handler; sigemptyset(&action.sa_mask); action.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &action, NULL); io = g_io_channel_unix_new(child_watch_pipe[0]); g_io_add_watch(io, G_IO_IN, child_watch, NULL); g_io_channel_unref(io);}static void exec_child(const gchar *working_directory, gchar **argv, gchar **envp, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, gpointer user_data){ int null; if (working_directory && chdir(working_directory) < 0) _exit(EXIT_FAILURE); if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN)) { int open_max, fd, ret; ret = 0; open_max = sysconf(_SC_OPEN_MAX); for (fd = 3; fd < open_max && ret == 0; fd++) ret = fcntl(fd, F_SETFD, FD_CLOEXEC); } null = open("/dev/null", O_RDWR); if (!(flags & G_SPAWN_CHILD_INHERITS_STDIN)) dup2(null, STDIN_FILENO); if (flags & G_SPAWN_STDOUT_TO_DEV_NULL) dup2(null, STDOUT_FILENO); if (flags & G_SPAWN_STDERR_TO_DEV_NULL) dup2(null, STDERR_FILENO); if (null > 2) close(null); if (child_setup) child_setup(user_data); if (envp) execve(argv[0], argv, envp); else execv(argv[0], argv); /* exec failed if we get here */ _exit(EXIT_FAILURE);}gboolean g_spawn_async(const gchar *working_directory, gchar **argv, gchar **envp, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, gpointer user_data, GPid *child_pid, GError **error){ GPid pid; if (access(argv[0], X_OK) < 0) { g_set_error(error, 0, 0, "%s is not executable", argv[0]); return FALSE; } if (child_watch_pipe[0] < 0) init_child_pipe(); /* Flush output streams so child doesn't get them */ fflush(NULL); switch (pid = fork()) { case -1: g_set_error(error, 0, 0, "fork failed: %s", strerror(errno)); return FALSE; case 0: exec_child(working_directory, argv, envp, flags, child_setup, user_data); break; default: if (child_pid) *child_pid = pid; return TRUE;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -